diff options
Diffstat (limited to 'Completion')
-rw-r--r-- | Completion/Unix/Command/_make | 346 |
1 files changed, 197 insertions, 149 deletions
diff --git a/Completion/Unix/Command/_make b/Completion/Unix/Command/_make index 95d7044e3..28b376bec 100644 --- a/Completion/Unix/Command/_make +++ b/Completion/Unix/Command/_make @@ -1,168 +1,216 @@ #compdef make gmake pmake dmake -local prev="$words[CURRENT-1]" file expl tmp is_gnu dir incl +# TODO: Based on targets given on the command line, show only variables that +# are used in those targets and their dependencies. + +local prev="$words[CURRENT-1]" file expl tmp is_gnu dir incl match +local -A TARGETS VARIABLES expandVars() { - local open close var val tmp=$2 ret=$2 - if (( $1 == 0 )); then - return - fi - while :; do - var=${tmp#*\$} - if [[ $var != $tmp ]]; then - tmp=$var - case $var in - (\(*) - open='(' - close=')' - ;; - ({*) - open='{' - close='}' - ;; - ([[:alpha:]]*) - open='' - close='' - var=${(s::)var[1]} - ;; - (\$*) - # avoid parsing second $ in $$ - tmp=${tmp#\$} - ;& - (*) - continue - ;; - esac - if [[ $open != '' ]]; then - var=${var#$open} + local open close var val front ret tmp=$1 + + front=${tmp%%\$*} + case $tmp in + (\(*) # Variable of the form $(foobar) + open='(' + close=')' + ;; + + ({*) # ${foobar} + open='{' + close='}' + ;; + + ([[:alpha:]]*) # $foobar. This is exactly $(f)oobar. + open='' + close='' + var=${(s::)var[1]} + ;; + + (\$*) # Escaped $. + print -- "${front}\$$(expandVars ${tmp#\$})" + return + ;; + + (*) # Nothing left to substitute. + print -- $tmp + return + ;; + esac + + if [[ -n $open ]] + then + var=${tmp#$open} var=${var%%$close*} - fi - case $var in - ([[:alnum:]_]#) - val=${(P)var} - val=$(expandVars $(($1 - 1)) $val) - ret=${ret//\$$open$var$close/$val} - ;; - esac - else - print -- ${ret//\$\$/\$} - return fi - done -} -# parseMakefile only runs inside $(...), so it doesn't matter that -# it pollutes the global namespace, setting zsh variables to -# make variables. The difficult case is where a make variable -# is special in zsh; we use local -h to hide those. This -# isn't a complete solution since it means variables defined in -# included Makefiles are undefined before returning to the parent. -parseMakefile() { - local input var val TAB=$'\t' dir=$1 - - while read input; do - case "$input " in - ([[:alnum:]][[:alnum:]_]#[ $TAB]#=*) - var=${input%%[ $TAB]#=*} - val=${input#*=} - val=${val##[ $TAB]#} - [[ ${(tP)var} = *special ]] && local -h $var - eval $var=\$val - ;; - ([[:alnum:]][[:alnum:]_]#[ $TAB]#:=*) - var=${input%%[ $TAB]#:=*} - val=${input#*=} - val=${val##[ $TAB]#} - val=$(expandVars 10 $val) - [[ ${(tP)var} = *special ]] && local -h $var - eval $var=\$val - ;; - ([[:alnum:]][^$TAB:=]#:[^=]*) - input=${input%%:*} - print $(expandVars 10 $input) - ;; - (${~incl} *) - local f=${input##${~incl} ##} - if [[ $incl = '.include' ]]; then - f=${f#[\"<]} - f=${f%[\">]} - fi - f=$(expandVars 10 $f) - case $f in - (/*) ;; - (*) f=$dir/$f ;; - esac - if [ -r $f ]; then - parseMakefile ${f%%/[^/]##} < $f - fi - ;; + case $var in + ([[:alnum:]_]#) + val=${VARIABLES[$var]} + ret=${ret//\$$open$var$close/$val} + ;; + + (*) # Improper variable name. No replacement. I'm not sure if this is desired behavior. + front+="\$$open$var$close" + ret=${ret/\$$open$var$close/} + ;; esac - done + + print -- "${front}$(expandVars ${ret})" +} + +parseMakefile () { + local input var val target dep TAB=$'\t' dir=$1 tmp + + while read input + do + case "$input " in + # VARIABLE = value + ([[:alnum:]][[:alnum:]_]#[ $TAB]#=*) + var=${input%%[ $TAB]#=*} + val=${input#*=} + val=${val##[ $TAB]#} + VARIABLES[$var]=$val + ;; + + # VARIABLE := value + # Evaluated immediately + ([[:alnum:]][[:alnum:]_]#[ $TAB]#:=*) + var=${input%%[ $TAB]#:=*} + val=${input#*=} + val=${val##[ $TAB]#} + val=$(expandVars $val) + VARIABLES[$var]=$val + ;; + + # TARGET: dependencies + # TARGET1 TARGET2 TARGET3: dependencies + ([[:alnum:]][^$TAB:=]#:[^=]*) + input=$(expandVars $input) + target=${input%%:*} + dep=${input#*:} + dep=${(z)dep} + dep="$dep" + for tmp in ${(z)target} + do + TARGETS[$tmp]=$dep + done + ;; + + # Include another makefile + (${~incl} *) + local f=${input##${~incl} ##} + if [[ $incl == '.include' ]] + then + f=${f#[\"<]} + f=${f%[\">]} + fi + f=$(expandVars $f) + case $f in + (/*) ;; + (*) f=$dir/$f ;; + esac + + if [[ -r $f ]] + then + parseMakefile ${f%%/[^/]##} < $f + fi + ;; + esac + done } findBasedir () { - local file index basedir - basedir=$PWD - for ((index=0; index<$#@; index++)); do - if [[ $@[index] = -C ]]; then - file=${~@[index+1]}; - if [[ -z $file ]]; then - # make returns with an error if an empty arg is given - # even if the concatenated path is a valid directory - return - elif [[ $file = /* ]]; then - # Absolute path, replace base directory - basedir=$file - else - # Relative, concatenate path - basedir=$basedir/$file - fi - fi - done - print -- $basedir + local file index basedir + basedir=$PWD + for (( index=0; index < $#@; index++ )) + do + if [[ $@[index] == -C ]] + then + file=${~@[index+1]}; + if [[ -z $file ]] + then + # make returns with an error if an empty arg is given + # even if the concatenated path is a valid directory + return + elif [[ $file == /* ]] + then + # Absolute path, replace base directory + basedir=$file + else + # Relative, concatenate path + basedir=$basedir/$file + fi + fi + done + print -- $basedir } _pick_variant -r is_gnu gnu=GNU unix -v -f -if [[ $is_gnu = gnu ]]; then - incl="(-|)include" +if [[ $is_gnu == gnu ]] +then + incl="(-|)include" else - incl=.include + incl=.include fi -if [[ "$prev" = -[CI] ]]; then - _files -W ${(q)$(findBasedir ${words[1,CURRENT-1]})} -/ -elif [[ "$prev" = -[foW] ]]; then - _files -W ${(q)$(findBasedir $words)} + +if [[ "$prev" == -[CI] ]] +then + _files -W ${(q)$(findBasedir ${words[1,CURRENT-1]})} -/ +elif [[ "$prev" == -[foW] ]] +then + _files -W ${(q)$(findBasedir $words)} else - file="$words[(I)-f]" - if (( file )); then - file=${~words[file+1]} - [[ $file = [^/]* ]] && file=${(q)$(findBasedir $words)}/$file - [[ -r $file ]] || file= - else - local basedir - basedir=${(q)$(findBasedir $words)} - if [[ $is_gnu = gnu && -r $basedir/GNUmakefile ]]; then - file=$basedir/GNUmakefile - elif [[ -r $basedir/makefile ]]; then - file=$basedir/makefile - elif [[ -r $basedir/Makefile ]]; then - file=$basedir/Makefile - else - file='' - fi - fi - - if [[ -n "$file" ]] && _tags targets; then - if [[ $is_gnu = gnu ]] && - zstyle -t ":completion:${curcontext}:targets" call-command; then - tmp=( $(_call_program targets "$words[1]" -nsp --no-print-directory -f "$file" .PHONY 2> /dev/null | parseMakefile $PWD) ) - else - tmp=( $(parseMakefile $PWD < $file) ) - fi - _wanted targets expl 'make target' compadd -a tmp && return 0 - fi - compstate[parameter]="${PREFIX%%\=*}" - compset -P 1 '*=' - _value "$@" + file="$words[(I)-f]" + if (( file )) + then + file=${~words[file+1]} + [[ $file == [^/]* ]] && file=${(q)$(findBasedir $words)}/$file + [[ -r $file ]] || file= + else + local basedir + basedir=${(q)$(findBasedir $words)} + if [[ $is_gnu == gnu && -r $basedir/GNUmakefile ]] + then + file=$basedir/GNUmakefile + elif [[ -r $basedir/makefile ]] + then + file=$basedir/makefile + elif [[ -r $basedir/Makefile ]] + then + file=$basedir/Makefile + else + file='' + fi + fi + + if [[ -n "$file" ]] + then + if [[ $is_gnu == gnu ]] && zstyle -t ":completion:${curcontext}:targets" call-command + then + parseMakefile $PWD < <(_call_program targets "$words[1]" -nsp --no-print-directory -f "$file" .PHONY 2> /dev/null) + else + parseMakefile $PWD < $file + fi + fi + + if [[ $PREFIX == (#b)([^=]##)'='* ]] && [[ -n ${${(k)VARIABLES}[(r)${match[1]}]} ]] + then + _message 'override make variable' + else + _tags targets variables + while _tags + do + _requested targets expl 'make targets' \ + compadd -- ${(k)TARGETS} + _requested variables expl 'make variables' \ + compadd -S '=' -- ${(k)VARIABLES} + done + fi + + # These are left over from the old completion. I'm not sure what they do. + #compstate[parameter]="${PREFIX%%\=*}" + #compset -P 1 '*=' + #_value "$@" fi |