Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
[PATCH] completion/_make: add completion for shell function generated targets
- X-seq: zsh-workers 53931
- From: Ling Wang <lingwang@xxxxxxxxxxx>
- To: zsh-workers@xxxxxxx
- Cc: Ling Wang <lingwang@xxxxxxxxxxx>
- Subject: [PATCH] completion/_make: add completion for shell function generated targets
- Date: Mon, 8 Sep 2025 21:45:47 +0800
- Arc-authentication-results: i=1; mx.zohomail.com; dkim=pass header.i=wcysite.com; spf=pass smtp.mailfrom=lingwang@xxxxxxxxxxx; dmarc=pass header.from=<lingwang@xxxxxxxxxxx>
- Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1757339212; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:MIME-Version:Message-ID:Subject:Subject:To:To:Message-Id:Reply-To; bh=aNUckZtjaIPbbgBOHn5cjcc+rcx2OzrIBeD+FzxoIdw=; b=Skm4LD7oPL7wZobqhO09Zl/WyoMjDlEmXKTLrvM7fmcw0lFTXnMvaQEjc3qKUr68TezhsA9yqahLz8CbkU7ZO65sjhxx8paOrNTtffCPbSmT2MuU6O9ee1RFofLSJT2o8NBDF4FxBctOsqtTfq31gykru1hy9/t815XDt3Q1vQU=
- Arc-seal: i=1; a=rsa-sha256; t=1757339212; cv=none; d=zohomail.com; s=zohoarc; b=CVXYONsgQjq8zdT4l4cF339UM4izfefxzvelTHeFG53Knv94oW7MZt55Uv9oNK1xpHTTcdwpLrc0e7fcDCVWhj9U/7EII/K1fED5mL1B+3sLDNdnZ6h33Vg71dbrPjgop+vTjjz5AToJzKXMEi7Yf2hCLk95J9/IGMiXikrC6gE=
- Archived-at: <https://zsh.org/workers/53931>
- List-id: <zsh-workers.zsh.org>
The gnu-make allows calling shell functions in variable assignment,
which then can be used to generate dynamic target. A simple example
like:
```
TARGETS := $(shell echo 'a b c') $(shell echo 'd')
$(TARGETS):
@echo "Target $@"
```
Which allows you to use `make a` and so on.
For now, the completion would shown as:
```
> make
$(shell echo 'a b c') $(shell echo 'd')
```
After modification, the completion would be:
```
> make
a b c d
```
Signed-off-by: Ling Wang <lingwang@xxxxxxxxxxx>
---
Completion/Unix/Command/_make | 109 +++++++++++++++++++++++++++-------
1 file changed, 88 insertions(+), 21 deletions(-)
diff --git a/Completion/Unix/Command/_make b/Completion/Unix/Command/_make
index 99c786dc7..014d4a3c7 100644
--- a/Completion/Unix/Command/_make
+++ b/Completion/Unix/Command/_make
@@ -3,8 +3,43 @@
# TODO: Based on targets given on the command line, show only variables that
# are used in those targets and their dependencies.
+_make-expandPossibleShellScripts() {
+ local open=$1 close=$2 rest=$3 script
+ # An possible shell call like this: $(shell echo '(((foo{bar})').
+ # As there can be nested (), {}, we need to parse them correctly.
+ local pos=9 in_paren=1 in_squote=0 in_dquote=0
+ # skip `\$\(shell ` part, the stack auto contains one '('
+ while [[ pos -le ${#rest} ]]; do
+ ch=$rest[$pos]
+ pos=$((pos + 1))
+ if [[ $ch == "'" && $in_dquote -eq 0 ]]; then
+ in_squote=$((1 - in_squote))
+ elif [[ $ch == '"' && $in_squote -eq 0 ]]; then
+ in_dquote=$((1 - in_dquote))
+ fi
+
+ if [[ $in_squote -eq 1 || $in_dquote -eq 1 ]]; then
+ continue
+ fi
+
+ if [[ $ch == '(' ]]; then
+ in_paren=$((in_paren + 1))
+ elif [[ $ch == ')' ]]; then
+ in_paren=$((in_paren - 1))
+ if [[ $in_paren -eq 0 ]]; then
+ # The stack is empty, end of shell script
+ script=${rest[9,$((pos - 2))]} # without `$(shell ` and `)`
+ print -r -- $script
+ return 0
+ fi
+ fi
+ done
+ print -- $rest # Failed in parsing, return original string
+ return 1
+}
+
_make-expandVars() {
- local open close var val front='' rest=$1
+ local open close var val script front='' rest=$1
while [[ $rest == (#b)[^$]#($)* ]]; do
front=$front${rest[1,$mbegin[1]-1]}
@@ -37,42 +72,74 @@ _make-expandVars() {
if [[ -n $open ]]; then
if [[ $rest == \$$open(#b)([[:alnum:]_]##)(#B)$close* ]]; then
- var=$match
+ var=$match
+ elif [[ $open == \( && $rest == \$$open(#b)shell\ (*)(#B)$close* ]]; then
+ script=$(_make-expandPossibleShellScripts $open $close $rest)
+ if [[ $? -eq 1 ]]; then
+ # Failed in parsing
+ print -- $front$rest
+ return 1
+ fi
else # unmatched () or {}, or bad parameter name
- print -- $front$rest
- return 1
+ print -- $front$rest
+ return 1
fi
fi
- val=''
- if [[ -n ${VAR_ARGS[(i)$var]} ]]; then
- val=${VAR_ARGS[$var]}
+ if [[ -n $script ]]; then
+ # We are expanding a shell script
+
+ # We shall process the makefile script to let escape works correctly
+ cmd=${script//\$\$/\$}
+ cmd=${cmd//\\\#/\#}
+ val=$(sh -c "$cmd")
+ val=${val//$'\n'/ }
+ rest=${rest//\$\(shell $script\)/$val}
else
- if [[ -n $opt_args[(I)(-e|--environment-overrides)] ]]; then
- if [[ $parameters[$var] == scalar-export* ]]; then
- val=${(P)var}
- elif [[ -n ${VARIABLES[(i)$var]} ]]; then
- val=${VARIABLES[$var]}
- fi
+ # We are expanding a simple variable
+ val=''
+ if [[ -n ${VAR_ARGS[(i)$var]} ]]; then
+ val=${VAR_ARGS[$var]}
else
- if [[ -n ${VARIABLES[(i)$var]} ]]; then
- val=${VARIABLES[$var]}
- elif [[ $parameters[$var] == scalar-export* ]]; then
- val=${(P)var}
- fi
+ if [[ -n $opt_args[(I)(-e|--environment-overrides)] ]]; then
+ if [[ $parameters[$var] == scalar-export* ]]; then
+ val=${(P)var}
+ elif [[ -n ${VARIABLES[(i)$var]} ]]; then
+ val=${VARIABLES[$var]}
+ fi
+ else
+ if [[ -n ${VARIABLES[(i)$var]} ]]; then
+ val=${VARIABLES[$var]}
+ elif [[ $parameters[$var] == scalar-export* ]]; then
+ val=${(P)var}
+ fi
+ fi
fi
+ rest=${rest//\$$open$var$close/$val}
fi
- rest=${rest//\$$open$var$close/$val}
done
print -- ${front}${rest}
}
_make-parseMakefile () {
- local input var val target dep TAB=$'\t' tmp IFS=
+ local input_front input var val target dep TAB=$'\t' tmp IFS=
- while read input
+ # We need -r to avoid interpreting backslashes, here is an example line:
+ # echo "1ab2" | sed -E "s/[0-9]*([a-z]+)[0-9]*/\1/"
+ # without -r, the backslash before 1 would be lost.
+ input_front=''
+ while read -r input
do
+ # However, this also means that backslashes are not line continuation.
+ # So, some tweaking is needed to handle line continuation.
+ if [[ $input == *'\' ]]; then
+ input_front=$input_front${input[1,-2]}
+ continue
+ fi
+ input=$input_front$input
+ input_front=''
+
case "$input " in
# VARIABLE = value OR VARIABLE ?= value
([[:alnum:]][[:alnum:]_]#[" "$TAB]#(\?|)=*)
--
2.51.0
Messages sorted by:
Reverse Date,
Date,
Thread,
Author