summaryrefslogtreecommitdiff
path: root/completions/make
blob: 96517c217a47d999614ae8b2af8d03e49fca7cfc (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
# bash completion for GNU make                             -*- shell-script -*-

_make_target_extract_script()
{
    local mode="$1"
    shift

    local prefix="$1"
    local prefix_pat=$(command sed 's/[][\,.*^$(){}?+|/]/\\&/g' <<<"$prefix")
    local basename=${prefix##*/}
    local dirname_len=$((${#prefix} - ${#basename}))

    if [[ $mode == -d ]]; then
        # display mode, only output current path component to the next slash
        local output="\2"
    else
        # completion mode, output full path to the next slash
        local output="\1\2"
    fi

    cat <<EOF
    1,/^# * Make data base/           d;        # skip any makefile output
    /^# * Finished Make data base/,/^# * Make data base/{
                                      d;        # skip any makefile output
    }
    /^# * Variables/,/^# * Files/     d;        # skip until files section
    /^# * Not a target/,/^$/          d;        # skip not target blocks
    /^${prefix_pat}/,/^$/!            d;        # skip anything user dont want

    # The stuff above here describes lines that are not
    #  explicit targets or not targets other than special ones
    # The stuff below here decides whether an explicit target
    #  should be output.

    /^# * File is an intermediate prerequisite/ {
      s/^.*$//;x;                               # unhold target
      d;                                        # delete line
    }

    /^$/ {                                      # end of target block
      x;                                        # unhold target
      /^$/d;                                    # dont print blanks
      s|^\(.\{${dirname_len}\}\)\(.\{${#basename}\}[^:/]*/\{0,1\}\)[^:]*:.*$|${output}|p;
      d;                                        # hide any bugs
    }

    # This pattern includes a literal tab character as \t is not a portable
    # representation and fails with BSD sed
    /^[^#	:%]\{1,\}:/ {         # found target block
      /^\.PHONY:/                 d;            # special target
      /^\.SUFFIXES:/              d;            # special target
      /^\.DEFAULT:/               d;            # special target
      /^\.PRECIOUS:/              d;            # special target
      /^\.INTERMEDIATE:/          d;            # special target
      /^\.SECONDARY:/             d;            # special target
      /^\.SECONDEXPANSION:/       d;            # special target
      /^\.DELETE_ON_ERROR:/       d;            # special target
      /^\.IGNORE:/                d;            # special target
      /^\.LOW_RESOLUTION_TIME:/   d;            # special target
      /^\.SILENT:/                d;            # special target
      /^\.EXPORT_ALL_VARIABLES:/  d;            # special target
      /^\.NOTPARALLEL:/           d;            # special target
      /^\.ONESHELL:/              d;            # special target
      /^\.POSIX:/                 d;            # special target
      /^\.NOEXPORT:/              d;            # special target
      /^\.MAKE:/                  d;            # special target
EOF

    # don't complete with hidden targets unless we are doing a partial completion
    if [[ -z ${prefix_pat} || ${prefix_pat} == */ ]]; then
        cat <<EOF
      /^${prefix_pat}[^a-zA-Z0-9]/d;            # convention for hidden tgt
EOF
    fi

    cat <<EOF
      h;                                        # hold target
      d;                                        # delete line
    }

EOF
}

_make()
{
    local cur prev words cword split
    _init_completion -s || return

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

    case $prev in
        --file | --makefile | --old-file | --assume-old | --what-if | --new-file | \
            --assume-new | -!(-*)[foW])
            _filedir
            return
            ;;
        --include-dir | --directory | -!(-*)[ICm])
            _filedir -d
            return
            ;;
        -!(-*)E)
            COMPREPLY=($(compgen -v -- "$cur"))
            return
            ;;
        --eval | -!(-*)[DVx])
            return
            ;;
        --jobs | -!(-*)j)
            COMPREPLY=($(compgen -W "{1..$(($(_ncpus) * 2))}" -- "$cur"))
            return
            ;;
    esac

    $split && return

    if [[ $cur == -* ]]; then
        local opts="$(_parse_help "$1")"
        COMPREPLY=($(compgen -W '${opts:-$(_parse_usage "$1")}' -- "$cur"))
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
    elif [[ $cur == *=* ]]; then
        prev=${cur%%=*}
        cur=${cur#*=}
        local diropt
        [[ ${prev,,} == *dir?(ectory) ]] && diropt=-d
        _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]} == -@(C|-directory) ]]; then
                # eval for tilde expansion
                eval "makef_dir=( -C \"${words[i + 1]}\" )"
                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]} == -@(f|-?(make)file) ]]; then
                # eval for tilde expansion
                eval "makef=( -f \"${words[i + 1]}\" )"
                break
            fi
        done

        # recognise that possible completions are only going to be displayed
        # so only the base name is shown
        local mode=--
        if ((COMP_TYPE != 9)); then
            mode=-d # display-only mode
        fi

        local IFS=$' \t\n' script=$(_make_target_extract_script $mode "$cur")
        COMPREPLY=($(LC_ALL=C \
            $1 -npq __BASH_MAKE_COMPLETION__=1 \
            ${makef+"${makef[@]}"} "${makef_dir[@]}" .DEFAULT 2>/dev/null |
            command sed -ne "$script"))

        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 _make make gmake gnumake pmake colormake bmake

# ex: filetype=sh