# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)


# NOTE: spack-completion.bash is auto-generated by:
#
#   $ spack commands --aliases --format=bash
#       --header=bash/spack-completion.bash --update=spack-completion.bash
#
# Please do not manually modify this file.


# The following global variables are set by Bash programmable completion:
#
#     COMP_CWORD:      An index into ${COMP_WORDS} of the word containing the
#                      current cursor position
#     COMP_KEY:        The key (or final key of a key sequence) used to invoke
#                      the current completion function
#     COMP_LINE:       The current command line
#     COMP_POINT:      The index of the current cursor position relative to the
#                      beginning of the current command
#     COMP_TYPE:       Set to an integer value corresponding to the type of
#                      completion attempted that caused a completion function
#                      to be called
#     COMP_WORDBREAKS: The set of characters that the readline library treats
#                      as word separators when performing word completion
#     COMP_WORDS:      An array variable consisting of the individual words in
#                      the current command line
#
# The following global variable is used by Bash programmable completion:
#
#     COMPREPLY:       An array variable from which bash reads the possible
#                      completions generated by a shell function invoked by the
#                      programmable completion facility
#
# See `man bash` for more details.

if test -n "${ZSH_VERSION:-}" ; then
  if [[ "$(emulate)" = zsh ]] ; then
    if ! typeset -f compdef >& /dev/null ; then
        # ensure base completion support is enabled, ignore insecure directories
        autoload -U +X compinit && compinit -i
    fi
    if ! typeset -f complete >& /dev/null ; then
        # ensure bash compatible completion support is enabled
        autoload -U +X bashcompinit && bashcompinit
    fi
    emulate sh -c "source '$0:A'"
    return # stop interpreting file
  fi
fi

# compgen -W doesn't work in some versions of zsh, so use this instead.
# see https://www.zsh.org/mla/workers/2011/msg00582.html
_compgen_w() {
    if test -n "${ZSH_VERSION:-}" ; then
        typeset -a words
        words=( ${~=1} )
        local find="$2"
        results=(${(M)words[@]:#$find*})
        echo "${results[@]}"
    else
        compgen -W "$1" -- "$2"
    fi
}

# Bash programmable completion for Spack
_bash_completion_spack() {
    # In all following examples, let the cursor be denoted by brackets, i.e. []

    # For our purposes, flags should not affect tab completion. For instance,
    # `spack install []` and `spack -d install --jobs 8 []` should both give the
    # same possible completions. Therefore, we need to ignore any flags in
    # COMP_WORDS. We do this by navigating the subcommand tree level-by-level: a
    # non-flag word is only kept if a completion function exists for the
    # resulting path.
    local -a COMP_WORDS_NO_FLAGS
    COMP_WORDS_NO_FLAGS=("spack")
    local subfunction="_spack"
    local index=1
    while [[ "$index" -lt "$COMP_CWORD" ]]
    do
        local word="${COMP_WORDS[$index]}"
        if [[ "$word" != -* ]]
        then
            local candidate="${subfunction}_${word//-/_}"
            if declare -f "$candidate" > /dev/null 2>&1
            then
                COMP_WORDS_NO_FLAGS+=("$word")
                subfunction="$candidate"
            fi
        fi
        ((index++))
    done

    # However, the word containing the current cursor position needs to be
    # added regardless of whether or not it is a flag. This allows us to
    # complete something like `spack install --keep-st[]`
    COMP_WORDS_NO_FLAGS+=("${COMP_WORDS[$COMP_CWORD]}")

    # Since we have removed all words after COMP_CWORD, we can safely assume
    # that COMP_CWORD_NO_FLAGS is simply the index of the last element
    local COMP_CWORD_NO_FLAGS=$((${#COMP_WORDS_NO_FLAGS[@]} - 1))

    # There is no guarantee that the cursor is at the end of the command line
    # when tab completion is invoked. For example, in the following situation:
    #     `spack -d [] install`
    # if the user presses the TAB key, a list of valid flags should be listed.
    # Note that we cannot simply ignore everything after the cursor. In the
    # previous scenario, the user should expect to see a list of flags, but
    # not of other subcommands. Obviously, `spack -d list install` would be
    # invalid syntax. To accomplish this, we use the variable list_options
    # which is true if the current word starts with '-' or if the cursor is
    # not at the end of the line.
    local list_options=false
    if [[ "${COMP_WORDS[$COMP_CWORD]}" == -* || "$COMP_POINT" -ne "${#COMP_LINE}" ]]
    then
        list_options=true
    fi

    # In general, when invoking tab completion, the user is not expecting to
    # see optional flags mixed in with subcommands or package names. Tab
    # completion is used by those who are either lazy or just bad at spelling.
    # If someone doesn't remember what flag to use, seeing single letter flags
    # in their results won't help them, and they should instead consult the
    # documentation. However, if the user explicitly declares that they are
    # looking for a flag, we can certainly help them out.
    #     `spack install -[]`
    # and
    #     `spack install --[]`
    # should list all flags and long flags, respectively. Furthermore, if a
    # subcommand has no non-flag completions, such as `spack arch []`, it
    # should list flag completions.

    local cur=${COMP_WORDS_NO_FLAGS[$COMP_CWORD_NO_FLAGS]}

    # If the cursor is in the middle of the line, like:
    #     `spack -d [] install`
    # COMP_WORDS will not contain the empty character, so we have to add it.
    if [[ "${COMP_LINE:$COMP_POINT-1:1}" == " " ]]
    then
        cur=""
    fi

    # Uncomment this line to enable logging
    #_test_vars >> temp

    $subfunction
    COMPREPLY=($(_compgen_w "$SPACK_COMPREPLY" "$cur"))

    # if every completion is an alias for the same thing, just return that thing.
    _spack_compress_aliases
}

# Helper functions for subcommands
# Results of each query are cached via environment variables

_subcommands() {
    if [[ -z "${SPACK_SUBCOMMANDS:-}" ]]
    then
        SPACK_SUBCOMMANDS="$(spack commands)"
    fi
    SPACK_COMPREPLY="$SPACK_SUBCOMMANDS"
}

_all_packages() {
    if [[ -z "${SPACK_ALL_PACKAGES:-}" ]]
    then
        SPACK_ALL_PACKAGES="$(spack list)"
    fi
    SPACK_COMPREPLY="$SPACK_ALL_PACKAGES"
}

_all_resource_hashes() {
    if [[ -z "${SPACK_ALL_RESOURCES_HASHES:-}" ]]
    then
        SPACK_ALL_RESOURCE_HASHES="$(spack resource list --only-hashes)"
    fi
    SPACK_COMPREPLY="$SPACK_ALL_RESOURCE_HASHES"
}

_installed_packages() {
    if [[ -z "${SPACK_INSTALLED_PACKAGES:-}" ]]
    then
        SPACK_INSTALLED_PACKAGES="$(spack --color=never find --no-groups)"
    fi
    SPACK_COMPREPLY="$SPACK_INSTALLED_PACKAGES"
}

_installed_compilers() {
    if [[ -z "${SPACK_INSTALLED_COMPILERS:-}" ]]
    then
        SPACK_INSTALLED_COMPILERS="$(spack compilers)"
    fi
    SPACK_COMPREPLY="$SPACK_INSTALLED_COMPILERS"
}

_providers() {
    if [[ -z "${SPACK_PROVIDERS:-}" ]]
    then
        SPACK_PROVIDERS="$(spack providers)"
    fi
    SPACK_COMPREPLY="$SPACK_PROVIDERS"
}

_mirrors() {
    if [[ -z "${SPACK_MIRRORS:-}" ]]
    then
        SPACK_MIRRORS="$(spack mirror list | awk '{print $1}')"
    fi
    SPACK_COMPREPLY="$SPACK_MIRRORS"
}

_repos() {
    if [[ -z "${SPACK_REPOS:-}" ]]
    then
        SPACK_REPOS="$(spack repo list --names)"
    fi
    SPACK_COMPREPLY="$SPACK_REPOS"
}

_unit_tests() {
    if [[ -z "${SPACK_TESTS:-}" ]]
    then
        SPACK_TESTS="$(spack unit-test -l)"
    fi
    SPACK_COMPREPLY="$SPACK_TESTS"
}

_environments() {
    if [[ -z "${SPACK_ENVIRONMENTS:-}" ]]
    then
        SPACK_ENVIRONMENTS="$(spack env list)"
    fi
    SPACK_COMPREPLY="$SPACK_ENVIRONMENTS"
}

_keys() {
    if [[ -z "${SPACK_KEYS:-}" ]]
    then
        SPACK_KEYS="$(spack gpg list)"
    fi
    SPACK_COMPREPLY="$SPACK_KEYS"
}

_config_sections() {
    if [[ -z "${SPACK_CONFIG_SECTIONS:-}" ]]
    then
        SPACK_CONFIG_SECTIONS="$(spack config list)"
    fi
    SPACK_COMPREPLY="$SPACK_CONFIG_SECTIONS"
}

_extensions() {
    if [[ -z "${SPACK_EXTENSIONS:-}" ]]
    then
        SPACK_EXTENSIONS="$(spack extensions)"
    fi
    SPACK_COMPREPLY="$SPACK_EXTENSIONS"
}

# Testing functions

# Function for unit testing tab completion
# Syntax: _spack_completions spack install py-
_spack_completions() {
    local COMP_CWORD COMP_KEY COMP_LINE COMP_POINT COMP_TYPE COMP_WORDS COMPREPLY

    # Set each variable the way bash would
    COMP_LINE="$*"
    COMP_POINT=${#COMP_LINE}
    COMP_WORDS=("$@")
    if [[ ${COMP_LINE: -1} == ' ' ]]
    then
        COMP_WORDS+=('')
    fi
    COMP_CWORD=$((${#COMP_WORDS[@]} - 1))
    COMP_KEY=9    # ASCII 09: Horizontal Tab
    COMP_TYPE=64  # ASCII 64: '@', to list completions if the word is not unmodified

    # Run Spack's tab completion function
    _bash_completion_spack

    # Return the result
    echo "${COMPREPLY[@]:-}"
}

# Log the environment variables used
# Syntax: _test_vars >> temp
_test_vars() {
    echo "-----------------------------------------------------"
    echo "Variables set by bash:"
    echo
    echo "COMP_LINE:                '$COMP_LINE'"
    echo "# COMP_LINE:              '${#COMP_LINE}'"
    echo "COMP_WORDS:               $(_pretty_print COMP_WORDS[@])"
    echo "# COMP_WORDS:             '${#COMP_WORDS[@]}'"
    echo "COMP_CWORD:               '$COMP_CWORD'"
    echo "COMP_KEY:                 '$COMP_KEY'"
    echo "COMP_POINT:               '$COMP_POINT'"
    echo "COMP_TYPE:                '$COMP_TYPE'"
    echo "COMP_WORDBREAKS:          '$COMP_WORDBREAKS'"
    echo
    echo "Intermediate variables:"
    echo
    echo "COMP_WORDS_NO_FLAGS:      $(_pretty_print COMP_WORDS_NO_FLAGS[@])"
    echo "# COMP_WORDS_NO_FLAGS:    '${#COMP_WORDS_NO_FLAGS[@]}'"
    echo "COMP_CWORD_NO_FLAGS:      '$COMP_CWORD_NO_FLAGS'"
    echo
    echo "Subfunction:              '$subfunction'"
    if $list_options
    then
        echo "List options:             'True'"
    else
        echo "List options:             'False'"
    fi
    echo "Current word:             '$cur'"
}

# Pretty-prints one or more arrays
# Syntax: _pretty_print array1[@] ...
_pretty_print() {
    for arg in $@
    do
        local array=("${!arg}")
        printf "$arg: ["
        printf   "'%s'" "${array[0]}"
        printf ", '%s'" "${array[@]:1}"
        echo "]"
    done
}

complete -o bashdefault -o default -F _bash_completion_spack spack

# Completion for spacktivate
complete -o bashdefault -o default -F _bash_completion_spack spacktivate

_spacktivate() {
  _spack_env_activate
}

# Simple function to get the spack alias for a command
_spack_get_alias() {
    local possible_alias="${1-}"
    local IFS=";"

    # spack aliases are a ;-separated list of :-separated pairs
    for item in $SPACK_ALIASES; do
        # maps a possible alias to its command
        eval "local real_command=\"\${item#*${possible_alias}:}\""
        if [ "$real_command" != "$item" ]; then
            SPACK_ALIAS="$real_command"
            return
        fi
    done

    # no alias found -- just return $1
    SPACK_ALIAS="$possible_alias"
}

# If all commands in COMPREPLY alias to the same thing, set COMPREPLY to
# just the real command, not the aliases.
_spack_compress_aliases() {
    # If there are zero or one completions, don't do anything
    # If this isn't the first argument, bail because aliases currently only apply
    # to top-level commands.
    if [ "${#COMPREPLY[@]}" -le "1" ] || [ "$COMP_CWORD_NO_FLAGS" != "1" ]; then
        return
    fi

    # get the alias of the first thing in the list of completions
    _spack_get_alias "${COMPREPLY[@]:0:1}"
    local first_alias="$SPACK_ALIAS"

    # if anything in the list would alias to something different, stop
    for comp in "${COMPREPLY[@]:1}"; do
        _spack_get_alias "$comp"
        if [ "$SPACK_ALIAS" != "$first_alias" ]; then
            return
        fi
    done

    # all commands alias to first alias; just return that
    COMPREPLY=("$first_alias")
}

# Spack commands
#
# Everything below here is auto-generated.
