summaryrefslogtreecommitdiffstats
path: root/completions/make
diff options
context:
space:
mode:
Diffstat (limited to 'completions/make')
-rw-r--r--completions/make214
1 files changed, 109 insertions, 105 deletions
diff --git a/completions/make b/completions/make
index 96517c2..94e2b73 100644
--- a/completions/make
+++ b/completions/make
@@ -1,135 +1,128 @@
# bash completion for GNU make -*- shell-script -*-
-_make_target_extract_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"
- shift
+ local mode=$1
+ local -x prefix=$2
- local prefix="$1"
- local prefix_pat=$(command sed 's/[][\,.*^$(){}?+|/]/\\&/g' <<<"$prefix")
- local basename=${prefix##*/}
- local dirname_len=$((${#prefix} - ${#basename}))
+ # display mode, only output current path component to the next slash
+ local -x prefix_replace=$prefix
+ [[ $mode == -d && $prefix == */* ]] &&
+ prefix_replace=${prefix##*/}
- 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
+ _comp_awk -f "${BASH_SOURCE[0]%/*}/../helpers/make-extract-targets.awk"
+}
- cat <<EOF
- h; # hold target
- d; # delete line
- }
+# 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
-EOF
+ 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
}
-_make()
+_comp_cmd_make()
{
- local cur prev words cword split
- _init_completion -s || return
+ 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 | -!(-*)[foW])
- _filedir
+ --assume-new | -${noargopts}[foW])
+ _comp_compgen_filedir
return
;;
- --include-dir | --directory | -!(-*)[ICm])
- _filedir -d
+ --include-dir | --directory | -${noargopts}[ICm])
+ _comp_compgen_filedir -d
return
;;
- -!(-*)E)
- COMPREPLY=($(compgen -v -- "$cur"))
+ -${noargopts}E)
+ _comp_compgen -- -v
return
;;
- --eval | -!(-*)[DVx])
+ --eval | -${noargopts}[DVx])
return
;;
- --jobs | -!(-*)j)
- COMPREPLY=($(compgen -W "{1..$(($(_ncpus) * 2))}" -- "$cur"))
+ --jobs | -${noargopts}j)
+ local REPLY
+ _comp_get_ncpus
+ _comp_compgen -- -W "{1..$((REPLY * 2))}"
return
;;
esac
- $split && return
+ [[ $was_split ]] && return
if [[ $cur == -* ]]; then
- local opts="$(_parse_help "$1")"
- COMPREPLY=($(compgen -W '${opts:-$(_parse_usage "$1")}' -- "$cur"))
+ _comp_compgen_help || _comp_compgen_usage
[[ ${COMPREPLY-} == *= ]] && compopt -o nospace
elif [[ $cur == *=* ]]; then
prev=${cur%%=*}
cur=${cur#*=}
local diropt
[[ ${prev,,} == *dir?(ectory) ]] && diropt=-d
- _filedir $diropt
+ _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]} == -@(C|-directory) ]]; then
- # eval for tilde expansion
- eval "makef_dir=( -C \"${words[i + 1]}\" )"
+ 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
@@ -137,25 +130,36 @@ _make()
# 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]}\" )"
+ 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
+ # 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)); then
- mode=-d # display-only mode
- fi
+ # if ((COMP_TYPE != 9 && COMP_TYPE != 37 && COMP_TYPE != 42)); then
+ # mode=-d # display-only mode
+ # fi
- local IFS=$' \t\n' script=$(_make_target_extract_script $mode "$cur")
- COMPREPLY=($(LC_ALL=C \
+ _comp_split COMPREPLY "$(LC_ALL=C \
$1 -npq __BASH_MAKE_COMPLETION__=1 \
${makef+"${makef[@]}"} "${makef_dir[@]}" .DEFAULT 2>/dev/null |
- command sed -ne "$script"))
+ _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
@@ -165,6 +169,6 @@ _make()
fi
} &&
- complete -F _make make gmake gnumake pmake colormake bmake
+ complete -F _comp_cmd_make make gmake gnumake pmake colormake bmake
# ex: filetype=sh