about summary refs log tree commit diff
path: root/Completion
diff options
context:
space:
mode:
Diffstat (limited to 'Completion')
-rw-r--r--Completion/Unix/Command/_make346
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