# bash completion for cmake(1)                             -*- shell-script -*-

_cmake()
{
    local is_old_completion=false
    local is_init_completion=false

    local cur prev words cword split was_split
    if type -t _comp_initialize >/dev/null; then
        _comp_initialize -s || return
    elif type -t _init_completion >/dev/null; then
        _init_completion -s || return
        is_init_completion=true
    else
        # manual initialization for older bash completion versions
        COMPREPLY=()
        cur="${COMP_WORDS[COMP_CWORD]}"
        prev="${COMP_WORDS[COMP_CWORD-1]}"
        is_old_completion=true
        split=false
    fi

    # Workaround for options like -DCMAKE_BUILD_TYPE=Release
    local prefix=
    if [[ $cur == -D* ]]; then
        prev=-D
        prefix=-D
        cur="${cur#-D}"
    elif [[ $cur == -U* ]]; then
        prev=-U
        prefix=-U
        cur="${cur#-U}"
    fi

    case "$prev" in
        -D)
            if [[ $cur == *=* ]]; then
            # complete values for variables
                local var type value
                var="${cur%%[:=]*}"
                value="${cur#*=}"

                if [[ $cur == CMAKE_BUILD_TYPE* ]]; then # most widely used case
                    COMPREPLY=( $( compgen -W 'Debug Release RelWithDebInfo
                        MinSizeRel' -- "$value" ) )
                    return
                fi

                if [[ $cur == *:* ]]; then
                    type="${cur#*:}"
                    type="${type%%=*}"
                else # get type from cache if it's not set explicitly
                    type=$( cmake -LA -N 2>/dev/null | grep "$var:" \
                        2>/dev/null )
                    type="${type#*:}"
                    type="${type%%=*}"
                fi
                case "$type" in
                    FILEPATH)
                        cur="$value"
                        _filedir
                        return
                        ;;
                    PATH)
                        cur="$value"
                        _filedir -d
                        return
                        ;;
                    BOOL)
                        COMPREPLY=( $( compgen -W 'ON OFF TRUE FALSE' -- \
                            "$value" ) )
                        return
                        ;;
                    STRING|INTERNAL)
                        # no completion available
                        return
                        ;;
                esac
            elif [[ $cur == *:* ]]; then
            # complete types
                local type="${cur#*:}"
                COMPREPLY=( $( compgen -W 'FILEPATH PATH STRING BOOL INTERNAL'\
                    -S = -- "$type" ) )
                compopt -o nospace
            else
            # complete variable names
                COMPREPLY=( $( compgen -W '$( cmake -LA -N 2>/dev/null |
                    tail -n +2 | cut -f1 -d: )' -P "$prefix" -- "$cur" ) )
                compopt -o nospace
            fi
            return
            ;;
        -U)
            COMPREPLY=( $( compgen -W '$( cmake -LA -N | tail -n +2 |
                cut -f1 -d: )' -P "$prefix" -- "$cur" ) )
            return
            ;;
    esac

    if $is_old_completion; then
        _split_longopt && split=true
    fi

    case "$prev" in
        -C|-P|--graphviz|--system-information)
            _filedir
            return
            ;;
        --build)
            # Seed the reply with non-directory arguments that we know are
            # allowed to follow --build. _filedir will then prepend any valid
            # directory matches to these.
            COMPREPLY=( $( compgen -W "--preset --list-presets" -- "$cur" ) )
            _filedir -d
            return
            ;;

        --target)
            local quoted
            printf -v quoted %q "$cur"
            # Options allowed right after `--target`
            local target_options='--verbose --clean-first --config --'
            local build_dir="build"
            for ((i=0; i < ${#COMP_WORDS[@]}; i++)); do
                if [[ "${COMP_WORDS[i]}" == "--build" ]]; then
                    build_dir="${COMP_WORDS[i+1]}"
                    break
                fi
            done
            if [[ "$cur" == -* ]]; then
                COMPREPLY=( $( compgen -W "$target_options" -- "$quoted" ) )
            else
                local targets=$( cmake --build "$build_dir" --target help 2>/dev/null \
                    | sed -n -e 's/^... \([^ ]*\).*$/\1/p' \
                       -e '/^\[/d' -e 's/^\([^ :]*\):.*$/\1/p' | \
                grep -v '^/' | sort -u )
                COMPREPLY=( $( compgen -W "$targets $target_options" -- "$quoted" ) )
            fi
            return
            ;;

        --install|--open)
            _filedir -d
            return
            ;;
        -E)
            COMPREPLY=( $( compgen -W "$( cmake -E help |& sed -n \
                '/^  [^ ]/{s|^  \([^ ]\{1,\}\) .*$|\1|;p}' 2>/dev/null )" \
                -- "$cur" ) )
            return
            ;;
        -G)
            local IFS=$'\n'
            local quoted
            printf -v quoted %q "$cur"
            COMPREPLY=( $( compgen -W '$( cmake --help 2>/dev/null | sed -n \
                -e "1,/^Generators/d" \
                -e "/^  *[^ =]/{s|^ *\([^=]*[^ =]\).*$|\1|;s| |\\\\ |g;p}" \
                2>/dev/null )' -- "$quoted" ) )
            return
            ;;
        --loglevel)
            COMPREPLY=( $(compgen -W 'error warning notice status verbose debug trace' -- $cur ) )
            ;;
        --help-command)
            COMPREPLY=( $( compgen -W '$( cmake --help-command-list 2>/dev/null|
                grep -v "^cmake version " )' -- "$cur" ) )
            return
            ;;
        --help-manual)
            COMPREPLY=( $( compgen -W '$( cmake --help-manual-list 2>/dev/null|
                grep -v "^cmake version " | sed -e "s/([0-9])$//" )' -- "$cur" ) )
            return
            ;;
        --help-module)
            COMPREPLY=( $( compgen -W '$( cmake --help-module-list 2>/dev/null|
                grep -v "^cmake version " )' -- "$cur" ) )
            return
            ;;
         --help-policy)
            COMPREPLY=( $( compgen -W '$( cmake --help-policy-list 2>/dev/null |
                grep -v "^cmake version " )' -- "$cur" ) )
            return
            ;;
         --help-property)
            COMPREPLY=( $( compgen -W '$( cmake --help-property-list \
                2>/dev/null | grep -v "^cmake version " )' -- "$cur" ) )
            return
            ;;
         --help-variable)
            COMPREPLY=( $( compgen -W '$( cmake --help-variable-list \
                2>/dev/null | grep -v "^cmake version " )' -- "$cur" ) )
            return
            ;;
        --list-presets)
            local IFS=$'\n'
            local quoted
            printf -v quoted %q "$cur"

            if [[ ! "${IFS}${COMP_WORDS[*]}${IFS}" =~ "${IFS}--build${IFS}" ]]; then
                COMPREPLY=(
                    $( compgen -W "configure${IFS}build${IFS}package${IFS}test${IFS}workflow${IFS}all" -- "$quoted" )
                  )
            fi
            return
            ;;
         --preset)
            local IFS=$'\n'
            local quoted
            printf -v quoted %q "$cur"

            local preset_type
            if [[ "${IFS}${COMP_WORDS[*]}${IFS}" =~ "${IFS}--workflow${IFS}" ]]; then
                preset_type="workflow"
            elif [[ "${IFS}${COMP_WORDS[*]}${IFS}" =~ "${IFS}--build${IFS}" ]]; then
                preset_type="build"
            else
                preset_type="configure"
            fi

            local presets=$( cmake --list-presets="$preset_type" 2>/dev/null |
                grep -o "^  \".*\"" | sed \
                -e "s/^  //g" \
                -e "s/\"//g" \
                -e 's/ /\\\\ /g' )
            COMPREPLY=( $( compgen -W "$presets" -- "$quoted" ) )
            return
            ;;
        --workflow)
            local quoted
            printf -v quoted %q "$cur"
            # Options allowed right after `--workflow`
            local workflow_options='--preset --list-presets --fresh'

            if [[ "$cur" == -* ]]; then
                COMPREPLY=( $( compgen -W "$workflow_options" -- "$quoted" ) )
            else
                local presets=$( cmake --list-presets=workflow 2>/dev/null |
                    grep -o "^  \".*\"" | sed \
                    -e "s/^  //g" \
                    -e "s/\"//g" \
                    -e 's/ /\\\\ /g' )
                COMPREPLY=( $( compgen -W "$presets $workflow_options" -- "$quoted" ) )
            fi
            return
            ;;
    esac

    if  ($is_old_completion || $is_init_completion); then
        $split && return
    else
        [[ $was_split ]] && return
    fi

    if [[ "$cur" == -* ]]; then
        # FIXME(#26100): `cmake --help` is missing some options and does not
        # have any mode-specific options like `cmake --build`'s `--config`.
        COMPREPLY=( $(compgen -W '$( _parse_help "$1" --help )' -- ${cur}) )
        [[ $COMPREPLY == *= ]] && compopt -o nospace
        [[ $COMPREPLY ]] && return
    fi

    _filedir
} &&
complete -F _cmake cmake

# ex: ts=4 sw=4 et filetype=sh
