Subject: Re: Completion in empty double-quotes generates error
From: Bart Schaefer <[email-masked]>
To: [email-masked]
Date: Wed, 6 Apr 2016 12:10:53 -0700
List-Id: Zsh Workers List <zsh-workers.zsh.org>

On Apr 2, 11:18am, Bart Schaefer wrote:
} Subject: Re: Completion in empty double-quotes generates error
}
} What would one expect to be completed with the cursor on the "2"?  The
} -redirect- special context is for what comes *after* the operator.  I
} can think of only two choices:
} 
} 1.  Complete as if there were an implicit space to the right of the
}     cursor.  This makes some sense because "2>" is treated as the same
}     single token as ">" all alone.
} 
} 2.  Treat it as an error and simply fail.
} 
} Patch below does this

More specifically, it attempted to do (1).  However, this situation is
really nasty -- get_comp_string() repeatedly calls the lexer looking for
the word containing the cursor, but because the lexer treats redirections
as positionally insignficant, there literally is no word containing the
cursor, so get_comp_string() keeps looking and finds the NEXT word (or
the end of the input), which places both wb and we beyond zlemetacs.

It gets worse still with "{myfd}>", and worse again when completing in
the whitespace between a word and a redirection (because in that last
situation it's correct for the redirection token to be ignored).

I'm still not entirely sure that the below is correct, i.e. that the
right things would happen if the "Should" comments were replaced with
the appropriate actual code.  But this at least handles more cases than
the previous situation.

The change in gotword() is necessary but of a little concern.  AFAICT
we only care about wb when examining the word under the cursor, so it
shouldn't hurt to skip updating it when doing so would violate the
wb <= zlemetacs <= we constraint, but there may be some corner case I
haven't tried that depends on the old behavior.

diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index b1709c1..1d4e1d2 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -1161,6 +1161,7 @@ get_comp_string(void)
     inpush(dupstrspace(linptr), 0, NULL);
     strinbeg(0);
     wordpos = cp = rd = ins = oins = linarr = parct = ia = redirpos = 0;
+    we = wb = zlemetacs;
     tt0 = NULLTOK;
 
     /* This loop is possibly the wrong way to do this.  It goes through *
@@ -1238,6 +1239,20 @@ get_comp_string(void)
 	    /* Record if we haven't had the command word yet */
 	    if (wordpos == redirpos)
 		redirpos++;
+	    if (zlemetacs < (zlemetall - inbufct) &&
+		zlemetacs >= wordbeg && wb == we) {
+		/* Cursor is in the middle of a redirection, treat as a word */
+		we = zlemetall - (inbufct + addedx);
+		if (addedx && we > wb) {
+		    /* Assume we are in {param}> form, wb points at "{" */
+		    wb++;
+		    /* Should complete parameter names here */
+		} else {
+		    /* In "2>" form, zlemetacs points at "2" */
+		    wb = zlemetacs;
+		    /* Should insert a space under cursor here */
+		}
+	    }
         }
 	if (tok == DINPAR)
 	    tokstr = NULL;
diff --git a/Src/lex.c b/Src/lex.c
index d4132fe..25b372a 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -1789,9 +1789,13 @@ parse_subst_string(char *s)
 static void
 gotword(void)
 {
-    we = zlemetall + 1 - inbufct + (addedx == 2 ? 1 : 0);
-    if (zlemetacs <= we) {
-	wb = zlemetall - wordbeg + addedx;
+    int nwe = zlemetall + 1 - inbufct + (addedx == 2 ? 1 : 0);
+    if (zlemetacs <= nwe) {
+	int nwb = zlemetall - wordbeg + addedx;
+	if (zlemetacs >= nwb) {
+	    wb = nwb;
+	    we = nwe;
+	}
 	lexflags = 0;
     }
 }