]> granicus.if.org Git - vim/commitdiff
patch 8.2.1079: Vim9: no line break allowed in a while loop v8.2.1079
authorBram Moolenaar <Bram@vim.org>
Sun, 28 Jun 2020 13:51:16 +0000 (15:51 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 28 Jun 2020 13:51:16 +0000 (15:51 +0200)
Problem:    Vim9: no line break allowed in a while loop.
Solution:   Update stored loop lines when finding line breaks.

src/eval.c
src/evalvars.c
src/ex_docmd.c
src/globals.h
src/proto/ex_docmd.pro
src/structs.h
src/testdir/test_vim9_cmd.vim
src/version.c

index 6aaa3a6ef0cab1298f61f275721884d9675c381d..82045675d01b66634072492cae7c34f11fec291e 100644 (file)
@@ -170,8 +170,11 @@ eval_to_bool(
 
     CLEAR_FIELD(evalarg);
     evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE;
-    evalarg.eval_cookie = eap != NULL && eap->getline == getsourceline
-                                                         ? eap->cookie : NULL;
+    if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline))
+    {
+       evalarg.eval_getline = eap->getline;
+       evalarg.eval_cookie = eap->cookie;
+    }
 
     if (skip)
        ++emsg_skip;
@@ -1840,10 +1843,9 @@ eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext)
            && evalarg != NULL
            && evalarg->eval_cookie != NULL
            && (*arg == NUL || (VIM_ISWHITE(arg[-1])
-                                            && (*arg == '"' || *arg == '#')))
-           && source_nextline(evalarg->eval_cookie) != NULL)
+                                            && (*arg == '"' || *arg == '#'))))
     {
-       char_u *p = source_nextline(evalarg->eval_cookie);
+       char_u *p = getline_peek(evalarg->eval_getline, evalarg->eval_cookie);
 
        if (p != NULL)
        {
@@ -1863,7 +1865,7 @@ eval_next_line(evalarg_T *evalarg)
     garray_T   *gap = &evalarg->eval_ga;
     char_u     *line;
 
-    line = getsourceline(0, evalarg->eval_cookie, 0, TRUE);
+    line = evalarg->eval_getline(0, evalarg->eval_cookie, 0, TRUE);
     if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK)
     {
        // Going to concatenate the lines after parsing.
@@ -5206,7 +5208,11 @@ ex_echo(exarg_T *eap)
 
     CLEAR_FIELD(evalarg);
     evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
-    evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL;
+    if (getline_equal(eap->getline, eap->cookie, getsourceline))
+    {
+       evalarg.eval_getline = eap->getline;
+       evalarg.eval_cookie = eap->cookie;
+    }
 
     if (eap->skip)
        ++emsg_skip;
index 1d6172ac7c3b07a30708d6c388f98cb2e257cd43..901d406a00c55708e577e55001bf6fedca64c3ae 100644 (file)
@@ -799,8 +799,11 @@ ex_let(exarg_T *eap)
                ++emsg_skip;
            CLEAR_FIELD(evalarg);
            evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
-           evalarg.eval_cookie = eap->getline == getsourceline
-                                                         ? eap->cookie : NULL;
+           if (getline_equal(eap->getline, eap->cookie, getsourceline))
+           {
+               evalarg.eval_getline = eap->getline;
+               evalarg.eval_cookie = eap->cookie;
+           }
            i = eval0(expr, &rettv, eap, &evalarg);
            if (eap->skip)
                --emsg_skip;
index 2469df341c687ad7048a73c14b0646253fdb3010..0ec63e24e491a7be76af982e8ed5f1b8aa8ca67a 100644 (file)
@@ -629,6 +629,7 @@ do_cmdline(
     cstack_T   cstack;                 // conditional stack
     garray_T   lines_ga;               // keep lines for ":while"/":for"
     int                current_line = 0;       // active line in lines_ga
+    int                current_line_before = 0;
     char_u     *fname = NULL;          // function or script name
     linenr_T   *breakpoint = NULL;     // ptr to breakpoint field in cookie
     int                *dbg_tick = NULL;       // ptr to dbg_tick field in cookie
@@ -851,27 +852,6 @@ do_cmdline(
            }
 # endif
        }
-
-       if (cstack.cs_looplevel > 0)
-       {
-           // Inside a while/for loop we need to store the lines and use them
-           // again.  Pass a different "fgetline" function to do_one_cmd()
-           // below, so that it stores lines in or reads them from
-           // "lines_ga".  Makes it possible to define a function inside a
-           // while/for loop.
-           cmd_getline = get_loop_line;
-           cmd_cookie = (void *)&cmd_loop_cookie;
-           cmd_loop_cookie.lines_gap = &lines_ga;
-           cmd_loop_cookie.current_line = current_line;
-           cmd_loop_cookie.getline = fgetline;
-           cmd_loop_cookie.cookie = cookie;
-           cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len);
-       }
-       else
-       {
-           cmd_getline = fgetline;
-           cmd_cookie = cookie;
-       }
 #endif
 
        // 2. If no line given, get an allocated line with fgetline().
@@ -929,21 +909,44 @@ do_cmdline(
 
 #ifdef FEAT_EVAL
        /*
-        * Save the current line when inside a ":while" or ":for", and when
-        * the command looks like a ":while" or ":for", because we may need it
-        * later.  When there is a '|' and another command, it is stored
-        * separately, because we need to be able to jump back to it from an
+        * Inside a while/for loop, and when the command looks like a ":while"
+        * or ":for", the line is stored, because we may need it later when
+        * looping.
+        *
+        * When there is a '|' and another command, it is stored separately,
+        * because we need to be able to jump back to it from an
         * :endwhile/:endfor.
+        *
+        * Pass a different "fgetline" function to do_one_cmd() below,
+        * that it stores lines in or reads them from "lines_ga".  Makes it
+        * possible to define a function inside a while/for loop and handles
+        * line continuation.
         */
-       if (current_line == lines_ga.ga_len
-               && (cstack.cs_looplevel || has_loop_cmd(next_cmdline)))
+       if ((cstack.cs_looplevel > 0 || has_loop_cmd(next_cmdline)))
        {
-           if (store_loop_line(&lines_ga, next_cmdline) == FAIL)
+           cmd_getline = get_loop_line;
+           cmd_cookie = (void *)&cmd_loop_cookie;
+           cmd_loop_cookie.lines_gap = &lines_ga;
+           cmd_loop_cookie.current_line = current_line;
+           cmd_loop_cookie.getline = fgetline;
+           cmd_loop_cookie.cookie = cookie;
+           cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len);
+
+           // Save the current line when encountering it the first time.
+           if (current_line == lines_ga.ga_len
+                   && store_loop_line(&lines_ga, next_cmdline) == FAIL)
            {
                retval = FAIL;
                break;
            }
+           current_line_before = current_line;
+       }
+       else
+       {
+           cmd_getline = fgetline;
+           cmd_cookie = cookie;
        }
+
        did_endif = FALSE;
 #endif
 
@@ -1078,7 +1081,7 @@ do_cmdline(
            else if (cstack.cs_lflags & CSL_HAD_LOOP)
            {
                cstack.cs_lflags &= ~CSL_HAD_LOOP;
-               cstack.cs_line[cstack.cs_idx] = current_line - 1;
+               cstack.cs_line[cstack.cs_idx] = current_line_before;
            }
        }
 
@@ -1515,7 +1518,7 @@ getline_cookie(
 {
 #ifdef FEAT_EVAL
     char_u             *(*gp)(int, void *, int, int);
-    struct loop_cookie *cp;
+    struct loop_cookie  *cp;
 
     // When "fgetline" is "get_loop_line()" use the "cookie" to find the
     // cookie that's originally used to obtain the lines.  This may be nested
@@ -1533,6 +1536,41 @@ getline_cookie(
 #endif
 }
 
+#if defined(FEAT_EVAL) || defined(PROT)
+/*
+ * Get the next line source line without advancing.
+ */
+    char_u *
+getline_peek(
+    char_u     *(*fgetline)(int, void *, int, int) UNUSED,
+    void       *cookie)                // argument for fgetline()
+{
+    char_u             *(*gp)(int, void *, int, int);
+    struct loop_cookie  *cp;
+    wcmd_T             *wp;
+
+    // When "fgetline" is "get_loop_line()" use the "cookie" to find the
+    // cookie that's originally used to obtain the lines.  This may be nested
+    // several levels.
+    gp = fgetline;
+    cp = (struct loop_cookie *)cookie;
+    while (gp == get_loop_line)
+    {
+       if (cp->current_line + 1 < cp->lines_gap->ga_len)
+       {
+           // executing lines a second time, use the stored copy
+           wp = (wcmd_T *)(cp->lines_gap->ga_data) + cp->current_line + 1;
+           return wp->line;
+       }
+       gp = cp->getline;
+       cp = cp->cookie;
+    }
+    if (gp == getsourceline)
+       return source_nextline(cp);
+    return NULL;
+}
+#endif
+
 
 /*
  * Helper function to apply an offset for buffer commands, i.e. ":bdelete",
index 81903818b0c7a323edf4428f161258a1ea7ad3db..19c6837ce282befaf58240765a38450e3052ab6c 100644 (file)
@@ -1885,7 +1885,7 @@ EXTERN listitem_T range_list_item;
 // Passed to an eval() function to enable evaluation.
 EXTERN evalarg_T EVALARG_EVALUATE
 # ifdef DO_INIT
-       = {EVAL_EVALUATE, NULL, {0, 0, 0, 0, NULL}, NULL}
+       = {EVAL_EVALUATE, NULL, NULL, {0, 0, 0, 0, NULL}, NULL}
 # endif
        ;
 #endif
index 7f5c691282267d2fc151a5b201f3d16fac208015..8f5ac10d4e3d96158b18a113947001ae585af0b1 100644 (file)
@@ -4,10 +4,11 @@ int do_cmdline_cmd(char_u *cmd);
 int do_cmdline(char_u *cmdline, char_u *(*fgetline)(int, void *, int, int), void *cookie, int flags);
 int getline_equal(char_u *(*fgetline)(int, void *, int, int), void *cookie, char_u *(*func)(int, void *, int, int));
 void *getline_cookie(char_u *(*fgetline)(int, void *, int, int), void *cookie);
+char_u *getline_peek(char_u *(*fgetline)(int, void *, int, int), void *cookie);
 int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only);
 int parse_cmd_address(exarg_T *eap, char **errormsg, int silent);
 int checkforcmd(char_u **pp, char *cmd, int len);
-char_u *find_ex_command(exarg_T *eap, int *full, void *((*lookup)(char_u *, size_t, cctx_T *)), cctx_T *cctx);
+char_u *find_ex_command(exarg_T *eap, int *full, void *(*lookup)(char_u *, size_t, cctx_T *), cctx_T *cctx);
 int modifier_len(char_u *cmd);
 int cmd_exists(char_u *name);
 cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);
index d68bc0184e25efb32851c53e0f7f536f67a2b4e0..fd5ae2aa211ab86c84251d65ff73d1dbc3d3cc2c 100644 (file)
@@ -1761,7 +1761,8 @@ typedef struct {
     int                eval_flags;     // EVAL_ flag values below
 
     // copied from exarg_T when "getline" is "getsourceline". Can be NULL.
-    void       *eval_cookie;   // argument for getline()
+    char_u     *(*eval_getline)(int, void *, int, int);
+    void       *eval_cookie;   // argument for eval_getline()
 
     // Used to collect lines while parsing them, so that they can be
     // concatenated later.  Used when "eval_ga.ga_itemsize" is not zero.
index a818d14084563ae68c2554469cf0d951223a0392..c0a4358d8dfd26f784f4134270e7fb815ad568f3 100644 (file)
@@ -131,12 +131,29 @@ def Test_if_linebreak()
 enddef
 
 def Test_while_linebreak()
-  " TODO: line break in :while expression doesn't work yet
   let lines =<< trim END
       vim9script
       let nr = 0
-      while nr < 10 + 3
-            nr = nr + 4
+      while nr <
+              10 + 3
+            nr = nr
+                  + 4
+      endwhile
+      assert_equal(16, nr)
+  END
+  CheckScriptSuccess(lines)
+
+  lines =<< trim END
+      vim9script
+      let nr = 0
+      while nr
+            <
+              10
+              +
+              3
+            nr = nr
+                  +
+                  4
       endwhile
       assert_equal(16, nr)
   END
index 4394d52b3486e6e188eee166b0748225f6dbc90b..8047cbe68a346bd4636ca2c1ab89f7ba6150b090 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1079,
 /**/
     1078,
 /**/