Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
[PATCH] comparguments: search more thoroughly for foreign-set options
- X-seq: zsh-workers 54876
- From: dana <dana@xxxxxxx>
- To: zsh-workers@xxxxxxx
- Subject: [PATCH] comparguments: search more thoroughly for foreign-set options
- Date: Mon, 29 Jun 2026 23:35:41 -0500
- Archived-at: <https://zsh.org/workers/54876>
- Feedback-id: i9be146f9:Fastmail
- List-id: <zsh-workers.zsh.org>
when searching for options in other sets we only look at whole words, so
stacked options etc don't match right:
compdef _foo foo
_foo() { _arguments -s : - set1 -a -b -c - set2 -d -e -f }
foo -ab <TAB> # still offers set2
this makes it match more thoroughly
there's a cost to this but i'm not sure how to measure it. i tried
timing `comparguments -i` with a large number of specs, but i didn't see
much of a difference. i'm not sure if that's because the test was bad or
because the poor set performance i've been warned about just isn't an
issue on modern pcs
dana
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 75f01521e..3e7755156 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -1722,6 +1722,8 @@ get_cadef(char *nam, char **args)
*
* "d" is a complete set of argument/option definitions to scan.
* "line" is the word we are scanning.
+ * "ign_active" indicates that whether an option def is active or not
+ * should be ignored when matching
* "full" indicates that the option must match a full word; otherwise
* we look for "=" arguments or prefixes.
* *"end" is set to point to the end of the option, in some cases
@@ -1729,24 +1731,25 @@ get_cadef(char *nam, char **args)
*/
static Caopt
-ca_get_opt(Cadef d, char *line, int full, char **end)
+ca_get_opt(Cadef d, char *line, int ign_active, int full, char **end)
{
Caopt p;
/* The full string may be an option. */
- for (p = d->opts; p; p = p->next)
- if (p->active && !strcmp(p->name, line)) {
+ for (p = d->opts; p; p = p->next) {
+ if ((ign_active || p->active) && !strcmp(p->name, line)) {
if (end)
*end = line + strlen(line);
return p;
}
+ }
if (!full) {
/* The string from the line probably only begins with an option. */
for (p = d->opts; p; p = p->next)
- if (p->active && ((!p->args || p->type == CAO_NEXT) ?
+ if ((ign_active || p->active) && ((!p->args || p->type == CAO_NEXT) ?
!strcmp(p->name, line) : strpfx(p->name, line))) {
int l = strlen(p->name);
if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) &&
@@ -1770,7 +1773,7 @@ ca_get_opt(Cadef d, char *line, int full, char **end)
/* Same as above, only for single-letter-style. */
static Caopt
-ca_get_sopt(Cadef d, char *line, char **end, LinkList *lp)
+ca_get_sopt(Cadef d, char *line, int ign_active, char **end, LinkList *lp)
{
Caopt p, pp = NULL;
char pre = *line++;
@@ -1779,8 +1782,8 @@ ca_get_sopt(Cadef d, char *line, char **end, LinkList *lp)
*lp = NULL;
for (p = NULL; *line; line++) {
- if ((sidx = single_index(pre, *line)) != -1 &&
- (p = d->single[sidx]) && p->active && p->args) {
+ if (d->single && (sidx = single_index(pre, *line)) != -1 &&
+ (p = d->single[sidx]) && (ign_active || p->active) && p->args) {
if (p->type == CAO_NEXT) {
if (!l)
*lp = l = newlinklist();
@@ -1796,7 +1799,7 @@ ca_get_sopt(Cadef d, char *line, char **end, LinkList *lp)
pp = p;
break;
}
- } else if (!p || (p && !p->active))
+ } else if (!p || (p && !(ign_active || p->active)))
return NULL;
pp = (p->name[0] == pre ? p : NULL);
p = NULL;
@@ -1813,16 +1816,15 @@ static int
ca_foreign_opt(Cadef curset, Cadef all, char *option)
{
Cadef d;
- Caopt p;
+ LinkList l;
for (d = all; d; d = d->snext) {
if (d == curset)
continue;
-
- for (p = d->opts; p; p = p->next) {
- if (!strcmp(p->name, option))
- return 1;
- }
+ if (ca_get_opt(d, option, 1, 0, NULL))
+ return 1;
+ if (ca_get_sopt(d, option, 1, NULL, &l))
+ return 1;
}
return 0;
}
@@ -1932,7 +1934,7 @@ ca_inactive(Cadef d, char **xor, int cur, int opts)
if (a && a->num == n && (!grp || (a->gsname &&
!strncmp(a->gsname, grp, grplen))))
a->active = 0;
- } else if ((opt = ca_get_opt(d, x, 1, NULL)) &&
+ } else if ((opt = ca_get_opt(d, x, 0, 1, NULL)) &&
(!grp || (opt->gsname && !strncmp(opt->gsname, grp, grplen))) &&
!(single && *opt->name && opt->name[1] && opt->name[2]))
opt->active = 0;
@@ -2181,7 +2183,7 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
/* See if it's an option. */
if (state.opt == 2 && (*line == '-' || *line == '+') &&
- (state.curopt = ca_get_opt(d, line, 0, &pe)) &&
+ (state.curopt = ca_get_opt(d, line, 0, 0, &pe)) &&
(state.curopt->type == CAO_OEQUAL ?
(compwords[cur] || pe[-1] == '=') :
(state.curopt->type == CAO_EQUAL ?
@@ -2230,7 +2232,7 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first)
}
} else if (state.opt == 2 && d->single &&
(*line == '-' || *line == '+') &&
- ((state.curopt = ca_get_sopt(d, line, &pe, &sopts)) ||
+ ((state.curopt = ca_get_sopt(d, line, 0, &pe, &sopts)) ||
(cur != compcurrent && sopts && nonempty(sopts)))) {
/* Or maybe it's a single-letter option? */
@@ -2812,7 +2814,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
subc = newlinklist();
while (lstate) {
- opt = ca_get_opt(lstate->d, args[1], 1, NULL);
+ opt = ca_get_opt(lstate->d, args[1], 0, 1, NULL);
if (opt && opt->args) {
ret = 0;
diff --git a/Test/Y03arguments.ztst b/Test/Y03arguments.ztst
index b118f2626..14a786709 100644
--- a/Test/Y03arguments.ztst
+++ b/Test/Y03arguments.ztst
@@ -728,6 +728,35 @@ F:should offer both sets of arguments in first case
>NO:{-d}
F:shouldn't offer -t in the first case (with stacked options)
+ tst_arguments -s : - set1 -a -b -c+: --d=: - set2 -e -f
+ comptest $'tst -ab -\t' # stack with known options
+ comptest $'tst -ax -\t' # stack with unknown option
+ comptest $'tst -cx -\t' # short option with same-word optarg
+ comptest $'tst --d=x -\t' # long option with same-word optarg
+0:detection of foreign-set option in stack or with same-word optarg
+>line: {tst -ab -}{}
+>DESCRIPTION:{option}
+>NO:{--d}
+>NO:{-c}
+>line: {tst -ax -}{}
+>DESCRIPTION:{option}
+>NO:{--d}
+>NO:{-a}
+>NO:{-b}
+>NO:{-c}
+>NO:{-e}
+>NO:{-f}
+>line: {tst -cx -}{}
+>DESCRIPTION:{option}
+>NO:{--d}
+>NO:{-a}
+>NO:{-b}
+>line: {tst --d=x -}{}
+>DESCRIPTION:{option}
+>NO:{-a}
+>NO:{-b}
+>NO:{-c}
+
tst_arguments -s '(set-a set--b grp-a grp--b grp-)-a' - set-a -s - set--b -t + grp-a -g + grp--b -h + grp- -i
comptest $'tst -a\t'
0:sets and groups with - in their name
Messages sorted by:
Reverse Date,
Date,
Thread,
Author