Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Completion heuristics (was Re: bug in _rpm?)



Bart Schaefer wrote:

> One approach would be to figure out some heuristic for determining that
> the ambiguous string "looks enough like" an element of the set of possible
> matches, and not insert it at all if it looks "too different."

Ok. Here is my first attempt at trying to avoid inserting unambiguous 
stuff that is only irritating.

Some explanation:

When inserting such an unambiguous string we use nested loops. The
outer one walks along all cline structs for the word-parts (resulting
from `*'-patterns). The inner one(s) then insert the stuff before or
after the anchors of the `*'-patterns.

I think our problem really only has to do with the first type of cline 
structs, i.e. for the word-parts its a all-or-nothing question. So we
only have to decide whether to insert such a top-level cline and its
prefix or suffix at all.

The string is inserted in the function `cline_str' (the name comes
from the fact that this function can also just return the unambiguous
string, without insering it into the line). I've now made this call
the new function cut_cline() (just before cline_str() in the code)
which looks at the whole cline list and, well, cuts this list at the
point where it thinks is the last interesting bit.

This decision is currently done as follows: all parts for which there
is something in the original string from the line are kept. E.g. with
possible matches `a.foo.c.1' and `a.bar.c.2' doing `a<TAB>' gives
`a.', but doing `a..<TAB> gives `a..c.' with the cursor on the second
dot.

Then, if only one part has missing characters, the whole unambiguous
string is inserted. If multiple parts have missing characters, anything
up to the first of them plus all parts directly after it without
missing characters are inserted. This means that there is still only
one part inside the string with missing characters (and probably one
at the end). However, more parts may be inserted because the code does 
two more checks (and now it gets a bit fuzzy):

For both tests it looks at structs from the point it found before
(first part with missing characters plus all parts without missing
characters directly after it) to the end. The first test tries to make 
sure that interestingly long unambiguous parts are inserted if that
doesn't make the whole string look too messy. It builds a value (`sum' 
in the code) that expresses how `good' the string would look up to the 
struct it currently handles. I compares the length of the unambiguous
part-string with the minimum and maximum lengths for this part (as
they appear in all matches added). For parts without missing
characters and for parts for which the unambiguous string has a
minimum length (two characters) and this length is at least as big as
the `(min + max) / 2', the `sum' value is increased (weighted by the
number of missing characters for the maximum length). For parts fo
which the unambiguous string is shorter than the minimum length, `sum' 
is decreased (and doubly so if the unambiguous string is shorter than
two characters because these are the particularly ugly looking empty
parts before one-letter anchors). Ok, while walking along the list it
remembers the point where `sum' had the greatest value and it was
greater than zero. If there was such a point it keeps the parts up to
it. If no part with `sum' greater than zero was found, it does the
second (and last test):

It sums up the lengths of all parts (after the one described above)
and if that sum is greater than 2/3 of the length of the shortest
match, the whole unambiguous string is inserted.


So, I think the first simple tests are good. The weighting thins looks 
good to me, too, although we probably want to fiddle with the
weighting itself. The last test is probably to simple. Maybe it should 
avoid overly simple unambiguous parts at the end. Maybe the test could 
be improved (the code now also calculates the length of the longest
match -- we could use this, too).

Anyway, with all this, we need some experience, so if you happen to
get something inserted which you didn't expect or don't like, please
let us know. And tell us the context, i.e. the original string, the
possible matches and the string that was inserted. And maybe your
thoughts about how this could be improved.


When you look at cut_cline() you'll see a harmless looking test at the 
top (`if (!hasmatched)...'), this is for handling the case where all
matches were added with `-U'. In that case we don't have any
information in the cline structs about the `original' string (of
course). This means that the very first test would always fail. So I
handled this specially: in this case the full unambiguous string is
inserted, even if it looks bad. My thought was that it may be better
to inserted that than to insert some truncated string and thereby
insert something where bits that originally were on the line are
missing (this has happened sometimes and is *very* irritating). Any
comments about this?

This also required some changes in the completion functions because
e.g. our friends `_path_files' and `_multi_parts' did exactly that --
do the matching with `compadd -D' or something like that and finally
add the matches with `-U'.


And finally, the patch contains some fixes in the matching code which
surfaced when I changed the completion functions. I could have sent
this separately, but since the code in cut_cline() really makes the
unambiguous string inserted look better I didn't bother to do that.

Bye
 Sven

diff -u os/Zle/zle_tricky.c Src/Zle/zle_tricky.c
--- os/Zle/zle_tricky.c	Sun Sep 19 12:21:02 1999
+++ Src/Zle/zle_tricky.c	Sun Sep 19 00:39:06 1999
@@ -159,6 +159,10 @@
 
 static int ispattern, haspattern;
 
+/* Non-zero if at least one match was added without -U. */
+
+static int hasmatched;
+
 /* Two patterns used when doing glob-completion.  The first one is built *
  * from the whole word we are completing and the second one from that    *
  * part of the word that was identified as a possible filename.          */
@@ -230,6 +234,10 @@
 
 static int mflags;
 
+/* Length of longest/shortest match. */
+
+static int maxmlen, minmlen;
+
 /* This holds the explanation strings we have to print in this group and *
  * a pointer to the current cexpl structure. */
 
@@ -287,6 +295,7 @@
     int olen;
     int slen;
     Cline prefix, suffix;
+    int min, max;
 };
 
 #define CLF_MISS  1
@@ -2003,6 +2012,39 @@
     return r;
 }
 
+/* Calculate the length of a cline and its sub-lists. */
+
+static int
+cline_sublen(Cline l)
+{
+    int len = ((l->flags & CLF_LINE) ? l->llen : l->wlen);
+
+    if (l->olen && !((l->flags & CLF_SUF) ? l->suffix : l->prefix))
+	len += l->olen;
+    else {
+	Cline p;
+
+	for (p = l->prefix; p; p = p->next)
+	    len += ((p->flags & CLF_LINE) ? p->llen : p->wlen);
+	for (p = l->suffix; p; p = p->next)
+	    len += ((p->flags & CLF_LINE) ? p->llen : p->wlen);
+    }
+    return len;
+}
+
+/* Set the lengths in the cline lists. */
+
+static void
+cline_setlens(Cline l, int both)
+{
+    while (l) {
+	l->max = cline_sublen(l);
+	if (both)
+	    l->min = l->max;
+	l = l->next;
+    }
+}
+
 /* This reverts the order of the elements of the given cline list and
  * returns a pointer to the new head. */
 
@@ -2217,8 +2259,8 @@
     if (!strncmp(l, w, wl))
 	l = NULL;
 
-    /* Split the new part into parts and turn the last one into a `suffix'
-     * if we have a left anchor. */
+    /* Split the new part into parts and turn the last one into a
+     * `suffix' if we have a left anchor. */
 
     p = bld_parts(s, sl, osl, &lp);
 
@@ -2234,8 +2276,21 @@
 	p = revert_cline(lp = p);
     /* Now add the sub-clines we already had. */
     if (matchsubs) {
-	matchlastsub->next = p->prefix;
-	p->prefix = matchsubs;
+	if (sfx) {
+	    Cline q;
+
+	    if ((q = lp->prefix)) {
+		while (q->next)
+		    q = q->next;
+		q->next = matchsubs;
+	    } else
+		lp->prefix = matchsubs;
+
+	    matchlastsub->next = NULL;
+	} else {
+	    matchlastsub->next = p->prefix;
+	    p->prefix = matchsubs;
+	}
 	matchsubs = matchlastsub = NULL;
     }
     /* Store the arguments in the last part-cline. */
@@ -2290,7 +2345,7 @@
 match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test)
 {
     int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw;
-    int il = 0, iw = 0, t, ind, add, bc = (bp ? *bp : 0);
+    int il = 0, iw = 0, t, ind, add, bc = (bp ? *bp : 0), he = 0;
     VARARR(unsigned char, ea, ll + 1);
     char *ow;
     Cmlist ms;
@@ -2382,8 +2437,11 @@
 		    for (t = 0, tp = w, ct = 0, ict = lw - alen + 1;
 			 ict;
 			 tp += add, ct++, ict--) {
-			if (both ||
-			    (pattern_match(ap, tp - moff, NULL, NULL) &&
+			if ((both &&
+			     (!ap || !test ||
+			      !pattern_match(ap, tp + aoff, NULL, NULL))) ||
+			    (!both &&
+			     pattern_match(ap, tp - moff, NULL, NULL) &&
 			     match_parts(l + aoff , tp - moff, alen))) {
 			    if (sfx) {
 				savw = tp[-zoff];
@@ -2409,7 +2467,7 @@
 
 		    /* Yes, add the strings and clines if this is a 
 		     * top-level call. */
-		    if (!test) {
+		    if (!test && (!he || (llen + alen))) {
 			char *op, *lp, *map, *wap, *wmp;
 			int ol;
 
@@ -2475,10 +2533,15 @@
 		    }
 		    ow = w;
 
-		    if (!llen && !alen)
+		    if (!llen && !alen) {
 			lm = mp;
-		    else
-			lm = NULL;
+			if (he)
+			    mp = NULL;
+			else
+			    he = 1;
+		    } else {
+			lm = NULL; he = 0;
+		    }
 		    break;
 		} else if (ll >= mp->llen && lw >= mp->wlen) {
 		    /* Non-`*'-pattern. */
@@ -2567,6 +2630,7 @@
 		    }
 		    ow = w;
 		    lm = NULL;
+		    he = 0;
 		    break;
 		}
 	    }
@@ -2586,6 +2650,7 @@
 		bp = NULL;
 	    }
 	    lm = NULL;
+	    he = 0;
 	} else {
 	    /* No matcher and different characters: l does not match w. */
 	    if (test)
@@ -3398,6 +3463,7 @@
 {
     if (!e->suffix && a->prefix) {
 	Cline op = e->prefix, n = NULL, *p = &n, t, ca;
+	int min = 0, max = 0;
 
 	for (; b != e; b = b->next) {
 	    if ((*p = t = b->prefix)) {
@@ -3407,6 +3473,8 @@
 	    }
 	    b->suffix = b->prefix = NULL;
 	    b->flags &= ~CLF_SUF;
+	    min += b->min;
+	    max += b->max;
 	    *p = b;
 	    p = &(b->next);
 	}
@@ -3419,13 +3487,22 @@
 
 	    if (anew) {
 		join_psfx(e, a, NULL, NULL, 0);
-		if (e->prefix)
+		if (e->prefix) {
+		    e->min += min;
+		    e->max += max;
 		    break;
+		}
 	    } else {
 		join_psfx(e, a, NULL, NULL, 0);
-		if (a->prefix)
+		if (a->prefix) {
+		    a->min += min;
+		    a->max += max;
 		    break;
+		}
 	    }
+	    min -= n->min;
+	    max -= n->max;
+
 	    n = n->next;
 	}
     }
@@ -3437,6 +3514,8 @@
 static Cline
 join_clines(Cline o, Cline n)
 {
+    cline_setlens(n, 1);
+
     /* First time called, just return the new list. On further invocations
      * we will get it as the first argument. */
     if (!o)
@@ -3562,7 +3641,17 @@
 		    }
 		}
 	    }
-	    /* Ok, they are equal, now join the sub-lists. */
+	    /* Ok, they are equal, now copy the information about the
+             * original string if needed, calculate minimum and maximum
+	     * lengths, and join the sub-lists. */
+	    if (!o->orig && !o->olen) {
+		o->orig = n->orig;
+		o->olen = n->olen;
+	    }
+	    if (n->min < o->min)
+		o->min = n->min;
+	    if (n->max > o->max)
+		o->max = n->max;
 	    if (o->flags & CLF_MID)
 		join_mid(o, n);
 	    else
@@ -3599,6 +3688,7 @@
     Cmatch cm;
     Aminfo ai = (alt ? fainfo : ainfo);
     int palen, salen, qipl, ipl, pl, ppl, qisl, isl, psl;
+    int sl, lpl, lsl, ml;
 
     palen = salen = qipl = ipl = pl = ppl = qisl = isl = psl = 0;
 
@@ -3791,6 +3881,16 @@
     if (!ai->firstm)
 	ai->firstm = cm;
 
+    sl = strlen(str);
+    lpl = (cm->ppre ? strlen(cm->ppre) : 0);
+    lsl = (cm->psuf ? strlen(cm->psuf) : 0);
+    ml = sl + lpl + lsl;
+
+    if (ml < minmlen)
+	minmlen = ml;
+    if (ml > maxmlen)
+	maxmlen = ml;
+
     /* Do we have an exact match? More than one? */
     if (exact) {
 	if (!ai->exact) {
@@ -3799,13 +3899,10 @@
 		/* If a completion widget is active, we make the exact
 		 * string available in `compstate'. */
 
-		int sl = strlen(str);
-		int lpl = (cm->ppre ? strlen(cm->ppre) : 0);
-		int lsl = (cm->psuf ? strlen(cm->psuf) : 0);
 		char *e;
 
 		zsfree(compexactstr);
-		compexactstr = e = (char *) zalloc(lpl + sl + lsl + 1);
+		compexactstr = e = (char *) zalloc(ml + 1);
 		if (cm->ppre) {
 		    strcpy(e, cm->ppre);
 		    e += lpl;
@@ -3886,7 +3983,9 @@
      * was invoked. */
     SWITCHHEAPS(compheap) {
 	HEAPALLOC {
-	    doadd = (!dat->apar && !dat->opar && !dat->dpar);
+	    if ((doadd = (!dat->apar && !dat->opar && !dat->dpar)) &&
+		(dat->aflags & CAF_MATCH))
+		hasmatched = 1;
 	    if (dat->apar)
 		aparl = newlinklist();
 	    if (dat->opar)
@@ -4587,6 +4686,9 @@
 				"yes" : "");
 	movetoend = ((cs == we || isset(ALWAYSTOEND)) ? 2 : 1);
 	showinglist = 0;
+	hasmatched = 0;
+	minmlen = 1000000;
+	maxmlen = -1;
 
 	/* Make sure we have the completion list and compctl. */
 	if (makecomplist(s, incmd, lst)) {
@@ -6211,6 +6313,9 @@
     untokenize(lpre);
     untokenize(lsuf);
 
+    if (!(cc->mask & CC_DELETE))
+	hasmatched = 1;
+
     /* Handle completion of files specially (of course). */
 
     if ((cc->mask & (CC_FILES | CC_DIRS | CC_COMMPATH)) || cc->glob) {
@@ -7413,6 +7518,86 @@
     return len;
 }
 
+/* This cuts the cline list before the stuff that isn't worth
+ * inserting in the line. */
+
+static Cline
+cut_cline(Cline l)
+{
+    Cline p, e = NULL, maxp = NULL;
+    int sum = 0, max = 0, tmp, ls = 0;
+
+    /* If no match was added with matching, we don't really know
+     * which parts of the unambiguous string are worth keeping,
+     * so for now we keep everything (in the hope that this
+     * produces a string containing at least everything that was 
+     * originally on the line). */
+
+    if (!hasmatched) {
+	cline_setlens(l, 0);
+	return l;
+    }
+    e = l = cp_cline(l);
+
+    /* First, search the last struct for which we have something on
+     * the line. Anything before that is kept. */
+
+    for (p = l; p; p = p->next)
+	if (p->orig || p->olen)
+	    e = p->next;
+
+    /* Then keep all structs without missing characters. */
+
+    while (e && !(e->flags & CLF_MISS))
+	e = e->next;
+
+    if (e) {
+	/* Then we see if there is another struct with missing
+	 * characters. If not, we keep the whole list. */
+
+	for (p = e->next; p && !(p->flags & CLF_MISS); p = p->next);
+
+	if (p) {
+	    for (p = e; p; p = p->next) {
+		if (!(p->flags & CLF_MISS))
+		    sum += p->max;
+		else {
+		    tmp = cline_sublen(p);
+		    if (tmp > 2 && tmp > ((p->max + p->min) >> 1))
+			sum += tmp - (p->max - tmp);
+		    else if (tmp < p->min)
+			sum -= (((p->max + p->min) >> 1) - tmp) << (tmp < 2);
+		}
+		if (sum > max) {
+		    max = sum;
+		    maxp = p;
+		}
+	    }
+	    if (max)
+		e = maxp;
+	    else {
+		int len = 0;
+
+		cline_setlens(l, 0);
+		ls = 1;
+
+		for (p = e; p; p = p->next)
+		    len += p->max;
+
+		if (len > ((minmlen << 1) / 3))
+		    return l;
+	    }
+	    e->line = e->word = NULL;
+	    e->llen = e->wlen = e->olen = 0;
+	    e->next = NULL;
+	}
+    }
+    if (!ls)
+	cline_setlens(l, 0);
+
+    return l;
+}
+
 /* This builds the unambiguous string. If ins is non-zero, it is
  * immediatly inserted in the line. Otherwise csp is used to return
  * the relative cursor position in the string returned. */
@@ -7421,11 +7606,13 @@
 cline_str(Cline l, int ins, int *csp)
 {
     Cline s;
-    int ocs = cs, ncs, pcs, pm, sm, d, b, i, j, li = 0;
+    int ocs = cs, ncs, pcs, pm, pmax, sm, smax, d, b, i, j, li = 0;
     int pl, sl, hasp, hass, ppos, spos, plen, slen;
 
+    l = cut_cline(l);
+
     ppos = spos = plen = slen = hasp = hass = 0;
-    pm = sm = d = b = pl = sl = -1;
+    pm = pmax = sm = smax = d = b = pl = sl = -1;
 
     /* Get the information about the brace beginning and end we have
      * to re-insert. */
@@ -7487,8 +7674,10 @@
 	}
 	/* Remember the position if this is the first prefix with
 	 * missing characters. */
-	if ((l->flags & CLF_MISS) && !(l->flags & CLF_SUF))
-	    pm = cs;
+	if ((l->flags & CLF_MISS) && !(l->flags & CLF_SUF) &&
+	    (pmax < (l->min - l->max))) {
+	    pm = cs; pmax = l->min - l->max;
+	}
 	pcs = cs;
 	/* Insert the anchor. */
 	if (l->flags & CLF_LINE)
@@ -7508,8 +7697,9 @@
 	if (l->flags & CLF_MISS) {
 	    if (l->flags & CLF_MID)
 		b = cs;
-	    else if (l->flags & CLF_SUF)
-		sm = cs;
+	    else if ((l->flags & CLF_SUF) && smax < (l->min - l->max)) {
+		sm = cs; smax = l->min - l->max;
+	    }
 	}
 	/* And now insert the suffix or the original string. */
 	if (l->olen && (l->flags & CLF_SUF) && !l->suffix) {
diff -u -r oldcompletion/Core/_multi_parts Completion/Core/_multi_parts
--- oldcompletion/Core/_multi_parts	Sun Sep 19 12:19:13 1999
+++ Completion/Core/_multi_parts	Fri Sep 17 22:07:50 1999
@@ -100,14 +100,16 @@
         matches=( "${(@M)matches:#${tmp1[1]}*}" )
 	tmp2=( "${(@M)matches:#${tmp1[1]}${sep}*}" )
 
+        PREFIX="$pref$PREFIX"
+
 	if (( $#tmp2 )); then
-	  compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-	          -p "$pref" -qS "$sep" - "$tmp1[1]"
+	  compadd "$group[@]" "$expl[@]" -p "$pref" -qS "$sep" \
+                  -M "r:|${sep}=* r:|=*" - "$tmp1[1]"
         else
-	  compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-	          -p "$pref" - "$tmp1[1]"
+	  compadd "$group[@]" "$expl[@]" -p "$pref" \
+                  -M "r:|${sep}=* r:|=*" - "$tmp1[1]"
         fi
-	return 1
+	return 0
       fi
     elif (( $#tmp1 )); then
 
@@ -118,6 +120,8 @@
       SUFFIX="$suf"
       compadd -O matches -M "r:|${sep}=* r:|=*" - "$matches[@]"
 
+      PREFIX="$pref"
+
       if [[ -n "$menu" ]]; then
         # With menucompletion we just add matches for the matching
         # components with the prefix we collected and the rest from the
@@ -125,11 +129,13 @@
 
         tmp2="$pre$suf"
         if [[ "$tmp2" = *${sep}* ]]; then
-          compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-	          -p "$pref" -s "${sep}${tmp2#*${sep}}" - "$tmp1[@]"
+          SUFFIX="${sep}${tmp2#*${sep}}$SUFFIX"
+          compadd "$group[@]" "$expl[@]" \
+                  -p "$pref" -s "${sep}${tmp2#*${sep}}" \
+                  -M "r:|${sep}=* r:|=*" - "$tmp1[@]"
         else
-          compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-	          -p "$pref" - "$tmp1[@]"
+          compadd "$group[@]" "$expl[@]" -p "$pref"\
+                  -M "r:|${sep}=* r:|=*" - "$tmp1[@]"
         fi
       else
         # With normal completion we add all matches one-by-one with
@@ -138,11 +144,14 @@
 
         for i in "${(@M)matches:#(${(j:|:)~tmp1})*}"; do
 	  if [[ "$i" = *${sep}* ]]; then
-            compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-	            -S '' -p "$pref" -s "${i#*${sep}}" - "${i%%${sep}*}${sep}"
+	    SUFFIX="${i#*${sep}}$suf"
+            compadd "$group[@]" "$expl[@]" -S '' \
+	            -p "$pref" -s "${i#*${sep}}" \
+                    -M "r:|${sep}=* r:|=*" - "${i%%${sep}*}${sep}"
           else
-            compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-	            -S '' -p "$pref" - "$i"
+	    SUFFIX="$suf"
+            compadd "$group[@]" "$expl[@]" -S '' -p "$pref" \
+                    -M "r:|${sep}=* r:|=*" - "$i"
           fi
         done
       fi
@@ -154,12 +163,15 @@
 
       [[ "$orig" = "$pref$pre$suf" ]] && return 1
 
+      PREFIX="$pref$pre"
+      SUFFIX="$suf"
+
       if [[ -n "$suf" ]]; then
-        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-	        -s "$suf" - "$pref$pre"
+        compadd "$group[@]" "$expl[@]" -s "$suf" \
+                -M "r:|${sep}=* r:|=*" - "$pref$pre"
       else
-        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-	        -S '' - "$pref$pre$suf"
+        compadd "$group[@]" "$expl[@]" -S '' \
+                -M "r:|${sep}=* r:|=*" - "$pref$pre$suf"
       fi
       return 0
     fi
@@ -184,9 +196,11 @@
     # unambiguous prefix and that differs from the original string,
     # we insert it.
 
+    PREFIX="$pref"
+    SUFFIX=""
+
     [[ -n "$pref" && "$orig" != "$pref" ]] &&
-        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-	        -S '' - "$pref"
+        compadd "$group[@]" "$expl[@]" -S '' -M "r:|${sep}=* r:|=*" - "$pref"
 
     return
   fi
diff -u -r oldcompletion/Core/_path_files Completion/Core/_path_files
--- oldcompletion/Core/_path_files	Sun Sep 19 12:19:13 1999
+++ Completion/Core/_path_files	Fri Sep 17 21:54:39 1999
@@ -24,7 +24,7 @@
 #    menucompletion.
 
 local linepath realpath donepath prepath testpath exppath
-local tmp1 tmp2 tmp3 tmp4 i orig pre suf tpre tsuf
+local tmp1 tmp2 tmp3 tmp4 i orig pre suf tpre tsuf opre osuf
 local pats haspats=no ignore group expl addpfx addsfx remsfx
 local nm=$compstate[nmatches] menu
 
@@ -118,6 +118,8 @@
 
 pre="$PREFIX"
 suf="$SUFFIX"
+opre="$PREFIX"
+osuf="$SUFFIX"
 orig="${PREFIX}${SUFFIX}"
 
 [[ $compstate[insert] = (*menu|[0-9]*) || -n "$_comp_correct" ||
@@ -291,6 +293,8 @@
 
       if [[ "$haspats" = no && -z "$tpre$tsuf" &&
 	"$pre" = */ && -z "$suf" ]]; then
+	PREFIX="$opre"
+	SUFFIX="$osuf"
         compadd -nQS '' - "$linepath$donepath$orig"
         tmp4=-
       fi
@@ -349,35 +353,40 @@
       # collected as the suffixes to make the completion code expand
       # it as far as possible.
 
+      PREFIX="$linepath${testpath:q}$PREFIX"
+
       if [[ -n $menu ]]; then
         [[ -n "$compconfig[path_cursor]" ]] && compstate[to_end]=''
         if [[ "$tmp3" = */* ]]; then
+	  SUFFIX="${SUFFIX}/${tmp3#*/}"
 	  compadd -Qf -p "$linepath${testpath:q}" -s "/${tmp3#*/}" \
 	          -W "$prepath$realpath$testpath" "$ignore[@]" \
 		  "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" -M 'r:|/=* r:|=*' \
-		  "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+		  "$group[@]" "$expl[@]" \
 		  - "${(@)${(@)tmp1%%/*}:q}"
 	else
 	  compadd -Qf -p "$linepath${testpath:q}" \
 	          -W "$prepath$realpath$testpath" "$ignore[@]" \
 		   "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \
-		   "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+		   "$group[@]" "$expl[@]" \
 		   - "${(@)tmp1:q}"
 	fi
       else
         if [[ "$tmp3" = */* ]]; then
+          tmp4="$SUFFIX"
           for i in "$tmp1[@]"; do
+	    SUFFIX="${tmp4}/${${i#*/}:q}"
 	    compadd -Qf -p "$linepath${testpath:q}" -s "/${${i#*/}:q}" \
 		    -W "$prepath$realpath$testpath" "$ignore[@]" \
 		    "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" -M 'r:|/=* r:|=*' \
-		    "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+		    "$group[@]" "$expl[@]" \
 		    - "${${i%%/*}:q}"
 	  done
         else
 	  compadd -Qf -p "$linepath${testpath:q}" \
 		  -W "$prepath$realpath$testpath" "$ignore[@]" \
 		  "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" -M 'r:|/=* r:|=*' \
-		  "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+		  "$group[@]" "$expl[@]" \
 		  - "${(@)tmp1:q}"
         fi
       fi
@@ -403,10 +412,11 @@
   done
 
   if [[ -z "$tmp4" ]]; then
+    PREFIX="$linepath${testpath:q}$PREFIX"
     compadd -Qf -p "$linepath${testpath:q}" \
 	    -W "$prepath$realpath$testpath" "$ignore[@]" \
 	    "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" -M 'r:|/=* r:|=*' \
-	    "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+	    "$group[@]" "$expl[@]" \
 	    - "${(@)tmp1:q}"
   fi
 done
@@ -419,7 +429,9 @@
 
 if [[ -n "$compconfig[path_expand]" &&
       $#exppaths -gt 0 && nm -eq compstate[nmatches] ]]; then
-  compadd -Q -S '' "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+  PREFIX="$linepath"
+  SUFFIX=""
+  compadd -Q -S '' "$group[@]" "$expl[@]" \
           -M 'r:|/=* r:|=*' -p "$linepath" - "$exppaths[@]"
 fi
 
diff -u -r oldcompletion/Core/_sep_parts Completion/Core/_sep_parts
--- oldcompletion/Core/_sep_parts	Sun Sep 19 12:19:13 1999
+++ Completion/Core/_sep_parts	Sun Sep 19 11:57:59 1999
@@ -18,7 +18,7 @@
 # `-X explanation' options.
 
 local str arr sep test testarr tmparr prefix suffixes matchers autosuffix
-local matchflags opt group expl nm=$compstate[nmatches]
+local matchflags opt group expl nm=$compstate[nmatches] opre osuf
 
 # Get the options.
 
@@ -34,6 +34,8 @@
 
 # Get the string from the line.
 
+opre="$PREFIX"
+osuf="$SUFFIX"
 str="$PREFIX$SUFFIX"
 SUFFIX=""
 prefix=""
@@ -144,6 +146,8 @@
 
 # Add the matches for each of the suffixes.
 
+PREFIX="$pre"
+SUFFIX="$suf"
 for i in "$suffixes[@]"; do
   compadd -U "$group[@]" "$expl[@]" "$matchers[@]" "$autosuffix[@]" \
           -i "$IPREFIX" -I "$ISUFFIX" -p "$prefix" -s "$i" - "$testarr[@]"

--
Sven Wischnowsky                         wischnow@xxxxxxxxxxxxxxxxxxxxxxx



Messages sorted by: Reverse Date, Date, Thread, Author