zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: allow multiple -D options to compadd
@ 2021-10-26 23:15 Oliver Kiddle
  0 siblings, 0 replies; only message in thread
From: Oliver Kiddle @ 2021-10-26 23:15 UTC (permalink / raw)
  To: Zsh workers

The -D option to compadd removes array elements at positions
corresponding to whatever completion words don't match in the specified
array. If you've got both an array of display strings and an array of
matches, it can be useful to remove relevant elements from both of these
arrays. Currently you either have to use compadd -D twice, use an array
of numbers or approach the problem some other way. I couldn't think of
any reason why compadd couldn't simply allow multiple -D options and
remove elements in all the passed arrays. This patch enables that.

The first change for xinput completion demonstrates a use.

Before this, get_user_var() was used to retrieve the arrays after first
checking that it doesn't start with '('. This function is common to
several compadd options and allows a choice of '( one two three )' style
words, a scalar converted to a one element array or an associative
array. None of that seemed useful with -D. Failures would only be
reported when it later tried to assign the new array value. Removing
elements based on position is meaningless for an associative array given
the lack of ordering. And then it tries to pair up whatever remained as
new keys/values.

I briefly tried to make it print an error for an argument that isn't an
array but I can't see how to safely pass that back out from the middle
of that function despite critical stuff like heap swaps. It now simply
drops any element that isn't a non-empty array. The old code wasn't
ideal either anyway. I'd be interested if someone with better
understanding of the zsh error functions has ideas.

Oliver

diff --git a/Completion/X/Command/_xinput b/Completion/X/Command/_xinput
index 00a976d5d..2bbadd65a 100644
--- a/Completion/X/Command/_xinput
+++ b/Completion/X/Command/_xinput
@@ -106,8 +106,7 @@ case $state in
 
     if [[ $PREFIX$SUFFIX = [^-]*[^0-9]* ]]; then
       # match based on the names but insert IDs
-      compadd "$expl[@]" -M 'b:=* m:{[:lower:]}={[:upper:]}' -D ids -a names
-      compadd "$expl[@]" -M 'b:=* m:{[:lower:]}={[:upper:]}' -D disp -a names
+      compadd "$expl[@]" -M 'b:=* m:{[:lower:]}={[:upper:]}' -D ids -D disp -a names
       compadd "$expl[@]" -U -ld disp -a ids && ret=0
 
       zstyle -s ":completion:${curcontext}:input-devices" insert-ids out || out=menu
diff --git a/Completion/Zsh/Command/_compadd b/Completion/Zsh/Command/_compadd
index 9c92cda76..4456cf71e 100644
--- a/Completion/Zsh/Command/_compadd
+++ b/Completion/Zsh/Command/_compadd
@@ -31,7 +31,7 @@ args=(
   '-n[hide matches in completion listing]'
   '-O+[populate array with matches instead of adding them]:array:_parameters -g "*array*"'
   '-A+[populate array with expanded matches instead of adding them]:array:_parameters -g "*array*"'
-  '-D+[delete elements from array corresponding to non-matching candidates]:array:_parameters -g "*array*"'
+  '*-D+[delete elements from array corresponding to non-matching candidates]:array:_parameters -g "*array*"'
 )
 
 case $service in
diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo
index 9f67f13b7..d32a0702f 100644
--- a/Doc/Zsh/compwid.yo
+++ b/Doc/Zsh/compwid.yo
@@ -725,6 +725,8 @@ As with tt(-O), the var(completions) are not added to the set of matches.
 Instead, whenever the var(n)th var(completion) does not
 match, the var(n)th element of the var(array) is removed.  Elements
 for which the corresponding var(completion) matches are retained.
+This option can be used more than once to remove elements from multiple
+arrays.
 )
 item(tt(-C))(
 This option adds a special match which expands to all other matches
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 2e3249b52..0c5fbd4f0 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -329,7 +329,7 @@ struct cadata {
     char *exp;			/* explanation (-X) */
     char *apar;			/* array to store matches in (-A) */
     char *opar;			/* array to store originals in (-O) */
-    char *dpar;			/* array to delete non-matches in (-D) */
+    char **dpar;		/* arrays to delete non-matches in (-D) */
     char *disp;			/* array with display lists (-d) */
     char *mesg;			/* message to show unconditionally (-x) */
     int dummies;               /* add that many dummy matches */
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index 63136854e..2f3d53834 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -2081,10 +2081,10 @@ addmatches(Cadata dat, char **argv)
     /* ms: "match string" - string to use as completion.
      * Overloaded at one place as a temporary. */
     char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL;
-    char **aign = NULL, **dparr = NULL, *oaq = autoq, *oppre = dat->ppre;
+    char **aign = NULL, ***dparr = NULL, *oaq = autoq, *oppre = dat->ppre;
     char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL, *ibuf = NULL;
     char **arrays = NULL;
-    int lpl, lsl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
+    int dind, lpl, lsl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
     int ppl = 0, psl = 0, ilen = 0;
     int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern;
     int isexact, doadd, ois = instring, oib = inbackt;
@@ -2092,7 +2092,7 @@ addmatches(Cadata dat, char **argv)
     struct cmlist mst;
     Cmlist oms = mstack;
     Patprog cp = NULL, *pign = NULL;
-    LinkList aparl = NULL, oparl = NULL, dparl = NULL;
+    LinkList aparl = NULL, oparl = NULL, *dparl = NULL;
     Brinfo bp, bpl = brbeg, obpl, bsl = brend, obsl;
     Heap oldheap;
 
@@ -2186,11 +2186,24 @@ addmatches(Cadata dat, char **argv)
 	if (dat->opar)
 	    oparl = newlinklist();
 	if (dat->dpar) {
-	    if (*(dat->dpar) == '(')
-		dparr = NULL;
-	    else if ((dparr = get_user_var(dat->dpar)) && !*dparr)
-		dparr = NULL;
-	    dparl = newlinklist();
+	    int darr = 0, dparlen = arrlen(dat->dpar);
+	    char **tail = dat->dpar + dparlen;
+
+	    dparr = (char ***)hcalloc((1 + dparlen) * sizeof(char **));
+	    dparl = (LinkList *)hcalloc((1 + dparlen) * sizeof(LinkList));
+	    queue_signals();
+	    while (darr < dparlen) {
+		if ((dparr[darr] = getaparam(dat->dpar[darr])) && *dparr[darr]) {
+		    dparr[darr] = arrdup(dparr[darr]);
+		    dparl[darr++] = newlinklist();
+		} else {
+		    /* swap in the last -D argument if we didn't get a non-empty array */
+		    dat->dpar[darr] = *--tail;
+		    *tail = NULL;
+		    --dparlen;
+	        }
+	    }
+	    unqueue_signals();
 	}
 	/* Store the matcher in our stack of matchers. */
 	if (dat->match) {
@@ -2507,8 +2520,10 @@ addmatches(Cadata dat, char **argv)
 		}
 		if (!addit) {
 		    compignored++;
-		    if (dparr && !*++dparr)
-			dparr = NULL;
+		    for (dind = 0; dparl && dparl[dind]; dind++) {
+			if (dparr[dind] && !*++dparr[dind])
+			    dparr[dind] = NULL;
+		    }
 		    goto next_array;
 		}
 	    }
@@ -2525,8 +2540,10 @@ addmatches(Cadata dat, char **argv)
 					   !(dat->flags & CMF_FILE) ? 1 : 2) : 0),
 					 &bpl, bcp, &bsl, bcs,
 					 &isexact))) {
-		if (dparr && !*++dparr)
-		    dparr = NULL;
+		for (dind = 0; dparl && dparl[dind]; dind++) {
+		    if (dparr[dind] && !*++dparr[dind])
+			dparr[dind] = NULL;
+		}
 		goto next_array;
 	    }
 	    if (doadd) {
@@ -2553,10 +2570,14 @@ addmatches(Cadata dat, char **argv)
 		    addlinknode(aparl, ms);
 		if (dat->opar)
 		    addlinknode(oparl, s);
-		if (dat->dpar && dparr) {
-		    addlinknode(dparl, *dparr);
-		    if (!*++dparr)
-			dparr = NULL;
+		if (dat->dpar) {
+		    for (dind = 0; dparl[dind]; dind++) {
+			if (dparr[dind]) {
+			    addlinknode(dparl[dind], *dparr[dind]);
+			    if (!*++dparr[dind])
+				dparr[dind] = NULL;
+			}
+		    }
 		}
 		free_cline(lc);
 	    }
@@ -2584,8 +2605,10 @@ addmatches(Cadata dat, char **argv)
 	    set_list_array(dat->apar, aparl);
 	if (dat->opar)
 	    set_list_array(dat->opar, oparl);
-	if (dat->dpar)
-	    set_list_array(dat->dpar, dparl);
+	if (dat->dpar) {
+	    for (dind = 0; dparl[dind]; dind++)
+		set_list_array(dat->dpar[dind], dparl[dind]);
+	}
 	if (dat->exp)
 	    addexpl(0);
 	if (!hasallmatch && (dat->aflags & CAF_ALL)) {
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index 71d114de9..67a60963e 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -607,6 +607,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
     char *oarg = NULL; /* argument of -o option */
     int added; /* return value */
     Cmatcher match = NULL;
+    size_t dparlen = 0, dparsize = 0; /* no. of -D options and array size */
 
     if (incompfunc != 1) {
 	zwarnnam(name, "can only be called from completion function");
@@ -614,7 +615,8 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
     }
     dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre = dat.mesg =
 	dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp =
-	dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL;
+	dat.ign = dat.exp = dat.apar = dat.opar = NULL;
+    dat.dpar = NULL;
     dat.match = NULL;
     dat.flags = 0;
     dat.aflags = CAF_MATCH;
@@ -741,7 +743,12 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 		e = "parameter name expected after -%c";
 		break;
 	    case 'D':
-		sp = &(dat.dpar);
+		if (dparsize <= dparlen + 1) {
+		    dparsize = (dparsize + 1) * 2;
+		    dat.dpar = (char **)zrealloc(dat.dpar, sizeof(char *) * dparsize);
+		}
+		sp = dat.dpar + dparlen++;
+		*sp = dat.dpar[dparlen] = NULL;
 		e = "parameter name expected after -%c";
 		break;
 	    case 'd':
@@ -768,11 +775,13 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
                 } else {
                     zwarnnam(name, "number expected after -%c", *p);
 		    zsfree(mstr);
+		    zfree(dat.dpar, dparsize);
                     return 1;
                 }
                 if (dat.dummies < 0) {
                     zwarnnam(name, "invalid number: %d", dat.dummies);
 		    zsfree(mstr);
+		    zfree(dat.dpar, dparsize);
                     return 1;
                 }
 		break;
@@ -782,6 +791,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 	    default:
 		zwarnnam(name, "bad option: -%c", *p);
 		zsfree(mstr);
+		zfree(dat.dpar, dparsize);
 		return 1;
 	    }
 	    if (sp) {
@@ -802,6 +812,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 		    /* Missing argument: argv[N] == "-X", argv[N+1] == NULL. */
 		    zwarnnam(name, e, *p);
 		    zsfree(mstr);
+		    zfree(dat.dpar, dparsize);
 		    return 1;
 		}
 		if (m) {
@@ -820,17 +831,21 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 
     if (mstr && (match = parse_cmatcher(name, mstr)) == pcm_err) {
 	zsfree(mstr);
+	zfree(dat.dpar, dparsize);
 	return 1;
     }
     zsfree(mstr);
 
     if (!*argv && !dat.group && !dat.mesg &&
-	!(dat.aflags & (CAF_NOSORT|CAF_UNIQALL|CAF_UNIQCON|CAF_ALL)))
+	!(dat.aflags & (CAF_NOSORT|CAF_UNIQALL|CAF_UNIQCON|CAF_ALL))) {
+	zfree(dat.dpar, dparsize);
 	return 1;
+    }
 
     dat.match = match = cpcmatcher(match);
     added = addmatches(&dat, argv);
     freecmatcher(match);
+    zfree(dat.dpar, dparsize);
 
     return added;
 }


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-10-26 23:15 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-26 23:15 PATCH: allow multiple -D options to compadd Oliver Kiddle

Code repositories for project(s) associated with this inbox:

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).