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

PATCH: expand existing $'...' in completion string



This is supposed to help completion of strings with unprintable/invalid
characters by expanding existing $'...' strings that are already on the
command line when completion starts.  You won't be surprised to hear
this is fairly hairy; there are lots of ill-documented offsets to get
right.  I've actually tried it out with normal characters.  For example,
h$'\x69'$'\x73' is treated as "his" and so completes to hist.c etc.
(The expression is unquoted and then requoted later; attempting to
preserve the original quoting would be highly complicated and didn't
seem worthwhile.)

I've tried to cover problem cases such as where the $' expression
isn't finished, or when the cursor is in the middle of it, by simply
leaving the expression alone.

It seemed to handle the following case:

% touch A\ B
% echo A$'\x20'<TAB>
% echo A\ B

at which point I decided it was time to leave well alone.

I'm sure you'll let me know if it doesn't work.

Index: Src/Zle/zle_tricky.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_tricky.c,v
retrieving revision 1.87
diff -u -r1.87 zle_tricky.c
--- Src/Zle/zle_tricky.c	29 May 2007 17:01:09 -0000	1.87
+++ Src/Zle/zle_tricky.c	30 Aug 2007 21:30:55 -0000
@@ -1611,7 +1611,90 @@
     /* While building the quoted form, we also clean up the command line. */
     for (p = s, i = wb, j = 0; *p; p++, i++) {
 	int skipchars;
-	if ((*p == String || *p == Qstring) && p[1] == Snull)
+	if (*p == String && p[1] == Snull) {
+	    char *pe;
+	    for (pe = p + 2; *pe && *pe != Snull && i + (pe - p) < zlemetacs;
+		 pe++)
+		;
+	    if (!*pe) {
+		/* no terminating Snull, can't substitute */
+		skipchars = 2;
+	    } else {
+		/*
+		 * Try and substitute the $'...' expression.
+		 */
+		int len, tlen;
+		char *t = getkeystring(p + 2, &len, GETKEYS_DOLLARS_QUOTE,
+				       NULL);
+		len += 2;
+		tlen = strlen(t);
+		skipchars = len - tlen;
+		/*
+		 * If this makes the line longer, we don't attempt
+		 * to substitute it.  This is because "we" don't
+		 * really understand what the heck is going on anyway
+		 * and have blindly copied the code here from
+		 * the sections below.
+		 */
+		if (skipchars >= 0) {
+		    /* Update the completion string */
+		    memcpy(p, t, tlen);
+		    /* Update the version of the line we are operating on */
+		    ocs = zlemetacs;
+		    zlemetacs = i;
+		    if (skipchars > 0) {
+			/* Move the tail of the completion string up. */
+			char *dptr = p + tlen;
+			char *sptr = p + len;
+			while ((*dptr++ = *sptr++))
+			    ;
+			/*
+			 * If the character is before the cursor, we need to
+			 * update the offset into the completion string to the
+			 * cursor position, too.  (Use ocs since we've hacked
+			 * zlemetacs at this point.)
+			 */
+			if (i < ocs)
+			    offs -= skipchars;
+			/* Move the tail of the line up */
+			foredel(skipchars);
+			/*
+			 * Update the offset into the command line to the
+			 * cursor position if that's after the current position.
+			 */
+			if ((zlemetacs = ocs) > i)
+			    zlemetacs -= skipchars;
+			/* Always update the word end. */
+			we -= skipchars;
+		    }
+		    /*
+		     * Copy the unquoted string into place, which
+		     * now has the correct size.
+		     */
+		    memcpy(zlemetaline + i, t, tlen);
+
+		    /*
+		     * Move both the completion string pointer
+		     * and the command line offset to the end of
+		     * the chunk we've copied in (minus 1 for
+		     * the end of loop increment).  The line
+		     * and completion string chunks are now the
+		     * same length.
+		     */
+		    p += tlen - 1;
+		    i += tlen - 1;
+		    continue;
+		} else {
+		    /*
+		     * We give up if the expansion is longer the original
+		     * string.  That's because "we" don't really have the
+		     * first clue how the completion system actually works.
+		     */
+		    skipchars = 2;
+		}
+	    }
+	}
+	else if (*p == Qstring && p[1] == Snull)
 	    skipchars = 2;
 	else if (inull(*p))
 	    skipchars = 1;


-- 
Peter Stephenson <p.w.stephenson@xxxxxxxxxxxx>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/



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