# (C) 2010-2025 magicant

# Completion script for the "cd" built-in command.
# Completion function "completion/cd" is used for the "dirs", "pushd", "popd"
# built-ins as well.

function completion/cd {

        typeset OPTIONS ARGOPT PREFIX
        OPTIONS=( #>#
        "--help"
        ) #<#
        case ${WORDS[1]} in
        (cd|pushd)
                OPTIONS=("$OPTIONS" #>#
                "e --ensure-pwd; exit with status 1 if the new PWD cannot be determined"
                "L --logical; keep symbolic links in the \$PWD variable as is"
                "P --physical; resolve symbolic links in the \$PWD variable"
                "--default-directory:; specify the default directory to change to"
                ) #<#
                if [ "${WORDS[1]}" = "pushd" ]; then
                        OPTIONS=("$OPTIONS" #>#
                        "--remove-duplicates; remove duplicates in the directory stack"
                        ) #<#
                fi
                ;;
        (dirs)
                OPTIONS=("$OPTIONS" #>#
                "c --clear; clear the directory stack completely"
                "v --verbose; print stack entries with their indices"
                ) #<#
                ;;
        esac

        typeset dirstack=false
        command -f completion//parseoptions -es
        case $ARGOPT in
        (-)
                case $TARGETWORD in
                (-*[[:digit:]]*)
                        ;;
                (*)
                        command -f completion//completeoptions
                        ;;
                esac
                dirstack=true
                ;;
        (--default-directory)
                command -f completion/cd::completedir
                ;;
        (*)
                case ${WORDS[1]} in (cd|pushd)
                        command -f completion/cd::completedir
                esac
                dirstack=true
                ;;
        esac

        if $dirstack; then
                case ${WORDS[1]} in (dirs|pushd|popd)
                        complete -P "$PREFIX" --dirstack-index
                esac
        fi

}

function completion/cd::completedir {

        # Part 1: complete directories relative to $PWD

        # -L or -P?
        typeset logical=true option
        for option in "${WORDS[2,-1]}"; do
                case $option in
                        (-L|--logical)  logical=true ;;
                        (-P|--physical) logical=false ;;
                        (--)            break ;;
                esac
        done

        typeset saveopts="$(set +o)"
        set +o glob

        typeset word="${TARGETWORD#"$PREFIX"}"
        typeset basename="${word##*/}"
        typeset dirname="${word%"$basename"}"
        if $logical; then
                typeset IFS=/
                complete -T -P "$PREFIX$dirname" -S / -- $(
                        set -o errreturn -o glob -o nullglob
                        typeset CDPATH=
                        if [ "$dirname" ]; then
                                command cd -L -- "$dirname" >/dev/null 2>&1
                        fi
                        candidates=("$basename"*/)
                        candidates=("${candidates%/}")
                        printf %s "${candidates[*]}"
                )
        else
                complete -T -P "$PREFIX" -S / -d
        fi

        # Part 2: complete directories relative to $CDPATH

        case $word in (/*|./*|../*)
                eval "$saveopts"
                return
        esac

        set -o noglob

        typeset IFS=: cdpath candidates
        for cdpath in ${CDPATH-}; do
                if [ "$cdpath" = "" ]; then
                        cdpath=.
                fi

                # list candidates by globbing
                set -o glob -o nullglob
                candidates=("${cdpath%/}/$word"*/)

                # remove $cdpath and trailing slash
                candidates=("${candidates%/}")
                candidates=("${candidates##"${cdpath%/}/"}")

                complete -T -P "$PREFIX$dirname" -S / \
                        -- "${candidates#"$dirname"}"
        done

        eval "$saveopts"

}


# vim: set ft=sh ts=8 sts=8 sw=8 et:
