Subject: Re: bufferwords() lexes a subshell in a shortloop repeat as a string
From: Daniel Shahaf <[email-masked]>
To: Peter Stephenson <[email-masked]>
Cc: [email-masked]
Date: Wed, 20 Jan 2016 07:47:53 +0000
List-Id: Zsh Workers List <zsh-workers.zsh.org>

Peter Stephenson wrote on Mon, Jan 18, 2016 at 10:45:48 +0000:
> There is some special casing in bufferwords() for loops, though, so
> maybe the case you want isn't far off working.

bufferwords() received the «(x)» as a STRING token, so I looked further
down, into gettok().  The attached patch seems to do the trick [see the
added tests].  However, to paraphrase Knuth, I only tested this code,
not proved it correct.  I'd appreciate a review.

Thanks for the (snipped) clarifications.

Cheers,

Daniel

diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst
index bcea980..b64c76e 100644
--- a/Test/D04parameter.ztst
+++ b/Test/D04parameter.ztst
@@ -479,6 +479,8 @@
     '(( 3 + 1 == 8 / 2 ))'
     'for (( i = 1 ; i < 10 ; i++ ))'
     '((0.25542 * 60) - 15)*60'
+    'repeat 3 (x)'
+    'repeat 3 (echo foo; echo bar)'
   )
   for string in $strings; do
     array=(${(z)string})
@@ -514,6 +516,20 @@
 >8:15:
 >9:):
 >10:*60:
+>1:repeat:
+>2:3:
+>3:(:
+>4:x:
+>5:):
+>1:repeat:
+>2:3:
+>3:(:
+>4:echo:
+>5:foo:
+>6:;:
+>7:echo:
+>8:bar:
+>9:):

   line=$'A line with # someone\'s comment\nanother line # (1 more\nanother one'
diff --git a/Src/lex.c b/Src/lex.c
index 0f260d0..2505dd6 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -267,9 +267,13 @@ zshlex(void)
 {
     if (tok == LEXERR)
 	return;
-    do
+    do {
+	if (inrepeat_)
+	    ++inrepeat_;
+	if (inrepeat_ == 3 && isset(SHORTLOOPS))
+	    incmdpos = 1;
 	tok = gettok();
-    while (tok != ENDINPUT && exalias());
+    } while (tok != ENDINPUT && exalias());
     nocorrect &= 1;
     if (tok == NEWLIN || tok == ENDINPUT) {
 	while (hdocs) {
@@ -1870,6 +1874,7 @@ exalias(void)
 		  zshlextext[0] == '}' && !zshlextext[1])) &&
 		(rw = (Reswd) reswdtab->getnode(reswdtab, zshlextext))) {
 		tok = rw->token;
+		inrepeat_ = (tok == REPEAT);
 		if (tok == DINBRACK)
 		    incond = 1;
 	    } else if (incond && !strcmp(zshlextext, "]]")) {
diff --git a/Src/parse.c b/Src/parse.c
index 4829e3a..49c1ac0 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -63,6 +63,12 @@ int isnewlin;
 /**/
 int infor;
 
+/* != 0 if we are after a repeat keyword; if it's nonzero it's a 1-based index
+ * of the current token from the last-seen command position */
+
+/**/
+int inrepeat_;
+
 /* != 0 if parsing arguments of typeset etc. */
 
 /**/
@@ -271,6 +277,7 @@ parse_context_save(struct parse_stack *ps, int toplevel)
     ps->incasepat = incasepat;
     ps->isnewlin = isnewlin;
     ps->infor = infor;
+    ps->inrepeat_ = inrepeat_;
     ps->intypeset = intypeset;
 
     ps->hdocs = hdocs;
@@ -305,6 +312,7 @@ parse_context_restore(const struct parse_stack *ps, int toplevel)
     incasepat = ps->incasepat;
     isnewlin = ps->isnewlin;
     infor = ps->infor;
+    inrepeat_ = ps->inrepeat_;
     intypeset = ps->intypeset;
 
     hdocs = ps->hdocs;
@@ -447,6 +455,7 @@ init_parse_status(void)
      * using the lexical analyser for strings as well as here.
      */
     incasepat = incond = inredir = infor = intypeset = 0;
+    inrepeat_ = 0;
     incmdpos = 1;
 }
 
@@ -1482,6 +1491,7 @@ par_while(int *cmplx)
 static void
 par_repeat(int *cmplx)
 {
+    /* ### what to do about inrepeat_ here? */
     int oecused = ecused, p;
 
     p = ecadd(0);
diff --git a/Src/zsh.h b/Src/zsh.h
index 0302d68..a398242 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2913,6 +2913,7 @@ struct parse_stack {
     int incasepat;
     int isnewlin;
     int infor;
+    int inrepeat_;
     int intypeset;
 
     int eclen, ecused, ecnpats;
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
index 86840bd..a3af234 100644
--- a/Src/Zle/zle_vi.c
+++ b/Src/Zle/zle_vi.c
@@ -65,7 +65,7 @@ char *vichgbuf;
 int viinsbegin;
 
 static struct modifier lastmod;
-static int inrepeat, vichgrepeat;
+static int inrepeat, vichgrepeat; /* that's why the trailing underscore */
 
 /**
  * im: >= 0: is an insertmode