summaryrefslogtreecommitdiff
path: root/Src
diff options
context:
space:
mode:
authorOliver Kiddle <okiddle@yahoo.co.uk>2019-05-07 23:24:49 +0200
committerOliver Kiddle <okiddle@yahoo.co.uk>2019-05-07 23:24:49 +0200
commitcd6fd2b0a3641774e7854ff8298d1d82643c4b4a (patch)
tree1b09fbc0dfed078e48c945352c6635a4a6bcbb04 /Src
parent5200637bda09e34da934e18f3c30f4b124d8d597 (diff)
downloadzsh-cd6fd2b0a3641774e7854ff8298d1d82643c4b4a.tar.gz
zsh-cd6fd2b0a3641774e7854ff8298d1d82643c4b4a.zip
44274: allow finer control of completion match soring with compadd's -o option
Diffstat (limited to 'Src')
-rw-r--r--Src/Zle/comp.h6
-rw-r--r--Src/Zle/compcore.c52
-rw-r--r--Src/Zle/complete.c59
3 files changed, 90 insertions, 27 deletions
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 3e9834560..743a2e3ac 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -90,6 +90,9 @@ struct cmgroup {
#define CGF_PACKED 32 /* LIST_PACKED for this group */
#define CGF_ROWS 64 /* LIST_ROWS_FIRST for this group */
#define CGF_FILES 128 /* contains file names */
+#define CGF_MATSORT 256 /* sort by match rather than by display string */
+#define CGF_NUMSORT 512 /* sort numerically */
+#define CGF_REVSORT 1024 /* sort in reverse */
/* This is the struct used to hold matches. */
@@ -300,6 +303,9 @@ struct menuinfo {
#define CAF_ARRAYS 32 /* compadd -a or -k: array/assoc parameter names */
#define CAF_KEYS 64 /* compadd -k: assoc parameter names */
#define CAF_ALL 128 /* compadd -C: _all_matches */
+#define CAF_MATSORT 256 /* compadd -o match: sort by match rather than by display string */
+#define CAF_NUMSORT 512 /* compadd -o numeric: sort numerically */
+#define CAF_REVSORT 1024 /* compadd -o numeric: sort in reverse */
/* Data for compadd and addmatches() */
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index 0a454ad5f..9b8545360 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -2080,6 +2080,9 @@ addmatches(Cadata dat, char **argv)
/* Select the group in which to store the matches. */
gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT : 0) |
+ ((dat->aflags & CAF_MATSORT) ? CGF_MATSORT : 0) |
+ ((dat->aflags & CAF_NUMSORT) ? CGF_NUMSORT : 0) |
+ ((dat->aflags & CAF_REVSORT) ? CGF_REVSORT : 0) |
((dat->aflags & CAF_UNIQALL) ? CGF_UNIQALL : 0) |
((dat->aflags & CAF_UNIQCON) ? CGF_UNIQCON : 0));
if (dat->group) {
@@ -3034,8 +3037,9 @@ begcmgroup(char *n, int flags)
HEAP_ERROR(p->heap_id);
}
#endif
- if (p->name &&
- flags == (p->flags & (CGF_NOSORT|CGF_UNIQALL|CGF_UNIQCON)) &&
+ if (p->name && flags ==
+ (p->flags & (CGF_NOSORT|CGF_UNIQALL|CGF_UNIQCON|
+ CGF_MATSORT|CGF_NUMSORT|CGF_REVSORT)) &&
!strcmp(n, p->name)) {
mgroup = p;
@@ -3118,32 +3122,35 @@ addexpl(int always)
/* The comparison function for matches (used for sorting). */
+static int matchorder;
+
/**/
static int
matchcmp(Cmatch *a, Cmatch *b)
{
- if ((*a)->disp && !((*a)->flags & CMF_MORDER)) {
- if ((*b)->disp) {
- if ((*a)->flags & CMF_DISPLINE) {
- if ((*b)->flags & CMF_DISPLINE)
- return strcmp((*a)->disp, (*b)->disp);
- else
- return -1;
- } else {
- if ((*b)->flags & CMF_DISPLINE)
- return 1;
- else
- return strcmp((*a)->disp, (*b)->disp);
- }
- }
- return -1;
+ const char *as, *bs;
+ int cmp = !!(*b)->disp - !!(*a)->disp;
+ int sortdir = (matchorder & CGF_REVSORT) ? -1 : 1;
+
+ /* if match sorting selected or we have no display strings */
+ if ((matchorder & CGF_MATSORT) || (!cmp && !(*a)->disp)) {
+ as = (*a)->str;
+ bs = (*b)->str;
+ } else {
+ if (cmp) /* matches with display strings come first */
+ return cmp;
+
+ cmp = ((*b)->flags & CMF_DISPLINE) - ((*a)->flags & CMF_DISPLINE);
+ if (cmp) /* sort one-per-line display strings first */
+ return cmp;
+
+ as = (*a)->disp;
+ bs = (*b)->disp;
}
- if ((*b)->disp && !((*b)->flags & CMF_MORDER))
- return 1;
- return zstrcmp((*a)->str, (*b)->str, (SORTIT_IGNORING_BACKSLASHES|
- (isset(NUMERICGLOBSORT) ?
- SORTIT_NUMERICALLY : 0)));
+ return sortdir * zstrcmp(as, bs, SORTIT_IGNORING_BACKSLASHES|
+ ((isset(NUMERICGLOBSORT) ||
+ matchorder & CGF_NUMSORT) ? SORTIT_NUMERICALLY : 0));
}
/* This tests whether two matches are equal (would produce the same
@@ -3205,6 +3212,7 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
} else {
if (!(flags & CGF_NOSORT)) {
/* Now sort the array (it contains matches). */
+ matchorder = flags;
qsort((void *) rp, n, sizeof(Cmatch),
(int (*) _((const void *, const void *)))matchcmp);
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index 1dc2b01c2..c2f46c7f5 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -558,12 +558,53 @@ parse_class(Cpattern p, char *iptr)
return iptr;
}
+static struct { char *name; int abbrev; int oflag; } orderopts[] = {
+ { "nosort", 2, CAF_NOSORT },
+ { "match", 3, CAF_MATSORT },
+ { "numeric", 3, CAF_NUMSORT },
+ { "reverse", 3, CAF_REVSORT }
+};
+
+/* Parse the option to compadd -o, if flags is non-NULL set it
+ * returns -1 if the argument isn't a valid ordering, 0 otherwise */
+
+/**/
+static int
+parse_ordering(const char *arg, int *flags)
+{
+ int o, fl = 0;
+ const char *next, *opt = arg;
+ do {
+ int found = 0;
+ next = strchr(opt, ',');
+ if (!next)
+ next = opt + strlen(opt);
+
+ for (o = sizeof(orderopts)/sizeof(*orderopts) - 1; o >= 0 &&
+ !found; --o)
+ {
+ if ((found = next - opt >= orderopts[o].abbrev &&
+ !strncmp(orderopts[o].name, opt, next - opt)))
+ fl |= orderopts[o].oflag;
+ }
+ if (!found) {
+ if (flags) /* default to "match" */
+ *flags = CAF_MATSORT;
+ return -1;
+ }
+ } while (*next && ((opt = next + 1)));
+ if (flags)
+ *flags |= fl;
+ return 0;
+}
+
/**/
static int
bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
{
struct cadata dat;
char *mstr = NULL; /* argument of -M options, accumulated */
+ char *oarg = NULL; /* argument of -o option */
int added; /* return value */
Cmatcher match = NULL;
@@ -572,7 +613,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
return 1;
}
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.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp =
dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL;
dat.match = NULL;
dat.flags = 0;
@@ -587,6 +628,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
}
for (p = *argv + 1; *p; p++) {
char *m = NULL; /* argument of -M option (this one only) */
+ int order = 0; /* if -o found (argument to which is optional) */
char **sp = NULL; /* the argument to an option should be copied
to *sp. */
const char *e; /* error message */
@@ -710,7 +752,11 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
dat.flags |= CMF_DISPLINE;
break;
case 'o':
- dat.flags |= CMF_MORDER;
+ /* we honour just the first -o option but need to skip
+ * over a valid argument to subsequent -o options */
+ order = oarg ? -1 : 1;
+ sp = &oarg;
+ /* no error string because argument is optional */
break;
case 'E':
if (p[1]) {
@@ -741,15 +787,18 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
if (sp) {
if (p[1]) {
/* Pasted argument: -Xfoo. */
- if (!*sp)
+ if (!*sp) /* take first option only */
*sp = p + 1;
- p += strlen(p+1);
+ if (!order || !parse_ordering(oarg, order == 1 ? &dat.aflags : NULL))
+ p += strlen(p+1);
} else if (argv[1]) {
/* Argument in a separate word: -X foo. */
argv++;
if (!*sp)
*sp = *argv;
- } else {
+ if (order && parse_ordering(oarg, order == 1 ? &dat.aflags : NULL))
+ --argv;
+ } else if (!order) {
/* Missing argument: argv[N] == "-X", argv[N+1] == NULL. */
zwarnnam(name, e, *p);
zsfree(mstr);