summaryrefslogtreecommitdiffstats
path: root/completions/make
blob: 94e2b73b9dee2e547df7315f906aa2eeec0c636c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# bash completion for GNU make                             -*- shell-script -*-

# Extract the valid target names starting with PREFIX from the output of
# `make -npq'
# @param mode    If this is `-d', the directory names already specified in
#                PREFIX are omitted in the output
# @param prefix  Prefix of the target names
_comp_cmd_make__extract_targets()
{
    local mode=$1
    local -x prefix=$2

    # display mode, only output current path component to the next slash
    local -x prefix_replace=$prefix
    [[ $mode == -d && $prefix == */* ]] &&
        prefix_replace=${prefix##*/}

    _comp_awk -f "${BASH_SOURCE[0]%/*}/../helpers/make-extract-targets.awk"
}

# Truncate the non-unique filepaths in COMPREPLY to only generate unique
# directories or files.  This function discards the files under subdirectories
# unless the path is unique under each subdirectory and instead generate the
# subdirectory path.  For example, when there are two candidates, "abc/def" and
# "abc/xyz", we generate "abc/" instead of generating both candidates directly.
# When there is only one candidate "abc/def", we generate the full path
# "abc/def".
#
# @var[in] cur
# @var[in] mode
# @var[in,out] COMPREPLY
_comp_cmd_make__truncate_non_unique_paths()
{
    local prefix=$cur
    [[ $mode == -d ]] && prefix=
    if ((${#COMPREPLY[@]} > 0)); then
        # collect the possible completions including the directory names in
        # `paths' and count the number of children of each subdirectory in
        # `nchild'.
        local -A paths nchild
        local target
        for target in "${COMPREPLY[@]}"; do
            local path=${target%/}
            while [[ ! ${paths[$path]+set} ]] &&
                paths[$path]=set &&
                [[ $path == "$prefix"*/* ]]; do
                path=${path%/*}
                nchild[$path]=$((${nchild[$path]-0} + 1))
            done
        done

        COMPREPLY=()
        local nreply=0
        for target in "${!paths[@]}"; do
            # generate only the paths that do not have a unique child and whose
            # all parent and ancestor directories have a unique child.
            ((${nchild[$target]-0} == 1)) && continue
            local path=$target
            while [[ $path == "$prefix"*/* ]]; do
                path=${path%/*}
                ((${nchild[$path]-0} == 1)) || continue 2
            done

            # suffix `/' when the target path is a subdiretory, which has
            # at least one child.
            COMPREPLY[nreply++]=$target${nchild[$target]+/}
        done
    fi
}

_comp_cmd_make()
{
    local cur prev words cword was_split comp_args
    _comp_initialize -s -- "$@" || return

    local makef makef_dir=("-C" ".") i

    local noargopts='!(-*|*[foWICmEDVxj]*)'
    # shellcheck disable=SC2254
    case $prev in
        --file | --makefile | --old-file | --assume-old | --what-if | --new-file | \
            --assume-new | -${noargopts}[foW])
            _comp_compgen_filedir
            return
            ;;
        --include-dir | --directory | -${noargopts}[ICm])
            _comp_compgen_filedir -d
            return
            ;;
        -${noargopts}E)
            _comp_compgen -- -v
            return
            ;;
        --eval | -${noargopts}[DVx])
            return
            ;;
        --jobs | -${noargopts}j)
            local REPLY
            _comp_get_ncpus
            _comp_compgen -- -W "{1..$((REPLY * 2))}"
            return
            ;;
    esac

    [[ $was_split ]] && return

    if [[ $cur == -* ]]; then
        _comp_compgen_help || _comp_compgen_usage
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
    elif [[ $cur == *=* ]]; then
        prev=${cur%%=*}
        cur=${cur#*=}
        local diropt
        [[ ${prev,,} == *dir?(ectory) ]] && diropt=-d
        _comp_compgen_filedir $diropt
    else
        # before we check for makefiles, see if a path was specified
        # with -C/--directory
        for ((i = 1; i < ${#words[@]}; i++)); do
            if [[ ${words[i]} == @(-${noargopts}C|--directory) ]]; then
                # Expand tilde expansion
                local REPLY
                _comp_dequote "${words[i + 1]-}" &&
                    [[ -d ${REPLY-} ]] &&
                    makef_dir=(-C "$REPLY")
                break
            fi
        done

        # before we scan for targets, see if a Makefile name was
        # specified with -f/--file/--makefile
        for ((i = 1; i < ${#words[@]}; i++)); do
            if [[ ${words[i]} == @(-${noargopts}f|--?(make)file) ]]; then
                # Expand tilde expansion
                local REPLY
                _comp_dequote "${words[i + 1]-}" &&
                    [[ -f ${REPLY-} ]] &&
                    makef=(-f "$REPLY")
                break
            fi
        done

        # recognise that possible completions are only going to be displayed so
        # only the base name is shown.
        #
        # Note: This is currently turned off because the test suite of
        # bash-completion conflicts with it; it uses "set show-all-if-ambiguous
        # on" (causing COMP_TYPE == 37) to retrieve the action completion
        # results, and also the compact form with only the basenames is not
        # essentially needed.  To re-enable it, please uncomment the following
        # if-statement.
        local mode=--
        # if ((COMP_TYPE != 9 && COMP_TYPE != 37 && COMP_TYPE != 42)); then
        #     mode=-d # display-only mode
        # fi

        _comp_split COMPREPLY "$(LC_ALL=C \
            $1 -npq __BASH_MAKE_COMPLETION__=1 \
            ${makef+"${makef[@]}"} "${makef_dir[@]}" .DEFAULT 2>/dev/null |
            _comp_cmd_make__extract_targets "$mode" "$cur")"

        _comp_cmd_make__truncate_non_unique_paths

        if [[ $mode != -d ]]; then
            # Completion will occur if there is only one suggestion
            # so set options for completion based on the first one
            [[ ${COMPREPLY-} == */ ]] && compopt -o nospace
        fi

    fi
} &&
    complete -F _comp_cmd_make make gmake gnumake pmake colormake bmake

# ex: filetype=sh