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

Re: [BUG] COMPLETE_IN_WORD fails to recognize brace_parameter context



On Fri, May 7, 2021 at 7:09 AM Marlon Richert <marlon.richert@xxxxxxxxx> wrote:
>
> This bug is similar to the one I reported in workers/47858.

Only superficially.

> This works correctly:
>
> % setopt completeinword
> % tst() { print "\n$compstate[context]"; zle -I }

I think there's something missing here for the test case.  A style or
a bindkey?  Something to run "tst"?

> % # place cursor after '${(' and press Tab:
> % : ${()foo}

This is actually a failure in the interpretation of the cursor
position within the C code.  An existing comment says:
 * We are still within the parameter flags.  There's no
 * point trying to do anything clever here with
 * parameter names.  Instead, just report that we are in
 * a brace parameter but let the completion function
 * decide what to do about it.

The issue was that "still within the flags" assumed complete_in_word
was not set.  An earlier test had a similar mistake when the closing
brace of the entire parameter expression was already present.  Patch
below attempts to fix those assumptions, but still leaves further
effort up to the completion function ... which currently completes
nothing when the parens are empty.  I'm leaving that for someone else
to work on.

I also expanded on an existing comment.
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index 5162d97dc..131e86825 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -1125,6 +1125,18 @@ check_param(char *s, int set, int test)
      *
      * TODO: passing s as a parameter while we get some mysterious
      * offset "offs" into it via a global sucks badly.
+     *
+     * From ../lex.c we know:
+     * wb is the beginning position of the current word in the line
+     * we is the position of the end of the current word in the line
+     * From zle_tricky.c we know:
+     * offs is position within the word where we are completing
+     *
+     * So wb + offs is the current cursor position if COMPLETE_IN_WORD
+     * is set, otherwise it is the end of the word (same as we).
+     * 
+     * Note below we are thus stepping backward from our completion
+     * position to find a '$' in the current word (if any).
      */ 
     for (p = s + offs; ; p--) {
 	if (*p == String || *p == Qstring) {
@@ -1171,13 +1183,13 @@ check_param(char *s, int set, int test)
 	    char *tb = b;
 
 	    /* If this is a ${...}, see if we are before the '}'. */
-	    if (!skipparens(Inbrace, Outbrace, &tb))
+	    if (!skipparens(Inbrace, Outbrace, &tb) && tb - s <= offs)
 		return NULL;
 
 	    /* Ignore the possible (...) flags. */
-	    b++, br++;
-	    if ((qstring ? skipparens('(', ')', &b) :
-		 skipparens(Inpar, Outpar, &b)) > 0) {
+	    tb = ++b, br++;
+	    if ((qstring ? skipparens('(', ')', &tb) :
+		 skipparens(Inpar, Outpar, &tb)) > 0 || tb - s >= offs) {
 		/*
 		 * We are still within the parameter flags.  There's no
 		 * point trying to do anything clever here with
@@ -1188,6 +1200,14 @@ check_param(char *s, int set, int test)
 		ispar = 2;
 		return NULL;
 	    }
+	    if ((qstring ? '(' : Inpar) == *b) {
+		/*
+		 * We are inside the braces but on the opening paren.
+		 * There is nothing useful to complete here?
+		 */
+		return NULL;
+	    } else
+		b = tb;	/* Skip over the flags */
 
 	    for (tb = p - 1; tb > s && *tb != Outbrace && *tb != Inbrace; tb--);
 	    if (tb > s && *tb == Inbrace && (tb[-1] == String || *tb == Qstring))


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