# pacman/makepkg completion by Andres Perera <andres87p gmail>
#
# Distributed under the terms of the GNU General Public License v3 or
# later.
#
#   Local variables: common core cur list long m o prev query r remove s
#                    short sync upgrade w
#
# vim: ft=sh sts=2 sw=2:

# Removes packages/files already present in the line
_arch_rem_selected() {
  local w r

  for (( w=0; w<${#COMP_WORDS[@]}-1; w++)); do
    if (( w == COMP_CWORD )) || [[ w == -* ]]; then
      continue
    fi
    for r in ${!COMPREPLY[@]}; do
      [[ ${COMP_WORDS[w]} == ${COMPREPLY[r]} ]] && unset 'COMPREPLY[r]'; break
    done
  done
}

# makepkg completion
_makepkg() {
  COMPREPLY=()

  local prev cur short long w o
  prev=${COMP_WORDS[COMP_CWORD-1]}
  cur=`_get_cword`

  short=( A C L R c d e f g h i m o p r s )

  long=(  --allsource --asroot --clean --cleancache --config --force
          --geninteg --help --holdver --ignorearch --install --log
          --nobuild --nocolor --noconfirm --nodeps --noextract
          --noprogressbar --repackage --rmdeps --skipinteg --source
          --syncdeps )

  if (( COMP_CWORD == 1 )) && [[ $cur != -[^-]* ]]; then
    COMPREPLY=($(compgen -W "$(echo "${short[@]/#/-}" "${long[@]}")" -- "$cur"))
    return 0
  elif [[ $prev == @(--config|-*[^-]p*) ]]; then
    _filedir; _arch_rem_selected; return 0
  else
    for w in "${!COMP_WORDS[@]}"; do
      (( w == COMP_CWORD || w == 0 )) && continue
      [[ ${COMP_WORDS[w]} == @(--@(help|cleancache)|-*[hC]*) ]] && return 0
      for o in "${!long[@]}"; do
        if [[ ${COMP_WORDS[w]} == ${long[o]} ]]; then
          unset 'long[o]'; break
        fi
      done
      for o in "${!short[@]}"; do
        if [[ ${COMP_WORDS[w]} == -*([^-])${short[o]}* ]]; then
          unset 'short[o]'
        fi
      done
    done
    COMPREPLY=($(compgen -W "$(echo "${short[@]/#/-}" "${long[@]}")" -- "$cur"))
    if [[ $cur ==  -[^-]* ]]; then
      COMPREPLY=($(compgen -W "$(echo "${short[@]/#/$cur}")" -- "$cur"))
    fi
  fi
}

# pacman packages
_pacman_pkg() {
  COMPREPLY=($(compgen -W "$( if [[ $2 ]]; then
                                command pacman -"$1"  |\
                                command sed 's, .*,,' |\
                                command sort -u
                              else
                                command pacman -"$1"
                              fi)"     --     "$cur" ))
}

# pacman options
_pacman() {
  COMPREPLY=()

  local prev cur query remove sync upgrade core common list w m o s
  prev=${COMP_WORDS[COMP_CWORD-1]}
  cur=`_get_cword`

  # These are splitted in a subshell with IFS=' '. No point in listing
  # them by \n since compgen -W can't handle spaces or new lines in
  # operands
  query=( '--changelog --check --deps --explicit --file --foreign --groups --info
           --list --owns --quiet --search --unrequired --upgrades' 'c e g i k l m o p q s t u' )

  remove=( '--cascade --dbonly --nodeps --nosave --recursive --unneeded' 'c k n s u' )

  sync=( '--asdeps --asexplicit --clean --downloadonly --force --groups --ignore --ignoregroup
          --info --list --needed --nodeps --print-uris --quiet --refresh --search --sysupgrade'
         'c f g i l p q s u w y' )

  upgrade=( '--asdeps --asexplicit --force --nodeps' f )

  common=( '--cachedir --config --dbpath --debug --help --logfile --noconfirm
            --noprogressbar --noscriptlet --root --verbose' 'b d h r v' )

  core=( --help --query --remove --sync --upgrade --version -Q -R -S -U -V -h )

  # First word check
  if (( COMP_CWORD==1 )) && [[ $cur != -[^-]* ]]; then
    COMPREPLY=($(compgen -W "$(echo "${core[@]}" '-h' '--help' )" -- "$cur"))
    return 0
  # Complete filenames for selected options
  elif [[ $prev == @(-*([^-])[br]*|--@(config|logfile|root|dbpath|cachedir)) ]]; then
    _filedir; _arch_rem_selected; return 0
  # Main option check
  else
    for m in 'Q query' 'R remove' 'S sync' 'U upgrade' 'null'; do
      [[ $m == null ]] && return 0
      [[ $COMP_LINE == @(* -*([^-])${m% *}*|* --${m#* }*) ]] && break
    done
  fi

  # See if $cur is an operand before calling pacman
  case $cur in
    --*)
      # Generate the list from the option arrays. eval is being using on
      # possible matches between query, remove, sync, upgrade and common
      # arrays; completely sanitized input
      command eval "list=($(command sed 's,.*,\${&[0]},' <<<"${m#* }"; IFS=' '; echo ${common[0]}))"
      # Parse previous options
      for w in "${!COMP_WORDS[@]}"; do
        (( w == COMP_CWORD || w == 0 )) && continue
        if [[ ${COMP_WORDS[w]} == --@(help|version) ]]; then
          return 0
        fi
        for o in "${!list[@]}"; do
          if [[ ${COMP_WORDS[w]} == ${list[o]} ]]; then
            unset 'list[o]'; break
          fi
        done
      done
    ;;

    # Check if it's a short option instead
    -*)
      # Use the short array
      command eval "list=($(command sed 's,.*,\${&[1]},' <<<"${m#* }"; IFS=' '; echo ${common[1]}))"
      for w in "${!COMP_WORDS[@]}"; do
        (( w == COMP_CWORD || w == 0 )) && continue
        if [[ ${COMP_WORDS[w]} == -*([^-])[hV]* ]]; then
          return 0
        fi
        for o in "${!list[@]}"; do
          if [[ ${COMP_WORDS[w]} == -*([^-])${list[o]}* ]]; then
            unset 'list[o]'
          fi
        done
      done
      # Allow special double options
      case ${m% *} in
        Q)  [[ $cur == *([^i])i*([^i]) ]] && s+=(i) ;;
        R)  [[ $cur == *([^d])d*([^d]) ]] && s+=(s) ;;
        S)  [[ $cur == *([^c])c*([^u]) ]] && s+=(c)
            [[ $cur == *([^u])u*([^u]) ]] && s+=(u) ;;
      esac
      list=("${list[@]/#/$cur}" "${s[@]/#/$cur}")
    ;;

    # The word isn't an operand; use pacman or file dir
    *)
      case ${m% *} in
        # The weak extglobs are due to a previous match already
        # confirming validity
        Q)
          case $COMP_LINE in
            @(* -*([^-])g* *|* --groups *))   _pacman_pkg Qgq sort    ;;
            @(* -*([^-])o* *|* --owns *))     _filedir                ;;
            @(* -*([^-])p* *|* --file *))     _filedir '*.pkg.tar.gz' ;;
            @(* -*([^-])u* *|* --upgrades *)) _pacman_pkg Quq         ;;
            *)                                _pacman_pkg Qq          ;;
          esac
        ;;
        R)
          _pacman_pkg Qq
        ;;
        S)
          case $COMP_LINE in
            @(* -*([^-])l* *|* --list *))   _pacman_pkg Sl sort ;;
            @(* -*([^-])g* *|* --groups *)) _pacman_pkg Sg      ;;
            *)                              _pacman_pkg Slq     ;;
          esac
        ;;
        U)
          _filedir '*.pkg.tar.gz'
        ;;
      esac
      _arch_rem_selected
      return 0
    ;;
  esac
  COMPREPLY=($(compgen -W "$(echo "${list[@]}")" -- "$cur"))
}

complete -F _makepkg -o filenames makepkg
complete -F _pacman  -o filenames pacman