Patch 8.2.1079
Problem:    Vim9: no line break allowed in a while loop.
Solution:   Update stored loop lines when finding line breaks.
Files:      src/structs.h, src/globals.h, src/eval.c, src/evalvars.c,
            src/ex_docmd.c, src/proto/ex_docmd.pro,
            src/testdir/test_vim9_cmd.vim


*** ../vim-8.2.1078/src/structs.h       2020-06-27 18:06:42.152575113 +0200
--- src/structs.h       2020-06-28 13:54:22.894023740 +0200
***************
*** 1761,1767 ****
      int               eval_flags;     // EVAL_ flag values below
  
      // copied from exarg_T when "getline" is "getsourceline". Can be NULL.
!     void      *eval_cookie;   // argument for getline()
  
      // Used to collect lines while parsing them, so that they can be
      // concatenated later.  Used when "eval_ga.ga_itemsize" is not zero.
--- 1761,1768 ----
      int               eval_flags;     // EVAL_ flag values below
  
      // copied from exarg_T when "getline" is "getsourceline". Can be NULL.
!     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.
*** ../vim-8.2.1078/src/globals.h       2020-06-27 18:06:42.152575113 +0200
--- src/globals.h       2020-06-28 14:16:27.071259926 +0200
***************
*** 1885,1891 ****
  // 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}
  # endif
        ;
  #endif
--- 1885,1891 ----
  // Passed to an eval() function to enable evaluation.
  EXTERN evalarg_T EVALARG_EVALUATE
  # ifdef DO_INIT
!       = {EVAL_EVALUATE, NULL, NULL, {0, 0, 0, 0, NULL}, NULL}
  # endif
        ;
  #endif
*** ../vim-8.2.1078/src/eval.c  2020-06-27 23:07:31.959995377 +0200
--- src/eval.c  2020-06-28 14:43:43.388563492 +0200
***************
*** 170,177 ****
  
      CLEAR_FIELD(evalarg);
      evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE;
!     evalarg.eval_cookie = eap != NULL && eap->getline == getsourceline
!                                                         ? eap->cookie : NULL;
  
      if (skip)
        ++emsg_skip;
--- 170,180 ----
  
      CLEAR_FIELD(evalarg);
      evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE;
!     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,1849 ****
            && evalarg != NULL
            && evalarg->eval_cookie != NULL
            && (*arg == NUL || (VIM_ISWHITE(arg[-1])
!                                            && (*arg == '"' || *arg == '#')))
!           && source_nextline(evalarg->eval_cookie) != NULL)
      {
!       char_u *p = source_nextline(evalarg->eval_cookie);
  
        if (p != NULL)
        {
--- 1843,1851 ----
            && evalarg != NULL
            && evalarg->eval_cookie != NULL
            && (*arg == NUL || (VIM_ISWHITE(arg[-1])
!                                            && (*arg == '"' || *arg == '#'))))
      {
!       char_u *p = getline_peek(evalarg->eval_getline, evalarg->eval_cookie);
  
        if (p != NULL)
        {
***************
*** 1863,1869 ****
      garray_T  *gap = &evalarg->eval_ga;
      char_u    *line;
  
!     line = getsourceline(0, evalarg->eval_cookie, 0, TRUE);
      if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK)
      {
        // Going to concatenate the lines after parsing.
--- 1865,1871 ----
      garray_T  *gap = &evalarg->eval_ga;
      char_u    *line;
  
!     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,5212 ****
  
      CLEAR_FIELD(evalarg);
      evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
!     evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL;
  
      if (eap->skip)
        ++emsg_skip;
--- 5208,5218 ----
  
      CLEAR_FIELD(evalarg);
      evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
!     if (getline_equal(eap->getline, eap->cookie, getsourceline))
!     {
!       evalarg.eval_getline = eap->getline;
!       evalarg.eval_cookie = eap->cookie;
!     }
  
      if (eap->skip)
        ++emsg_skip;
*** ../vim-8.2.1078/src/evalvars.c      2020-06-27 23:07:31.959995377 +0200
--- src/evalvars.c      2020-06-28 14:10:35.105052556 +0200
***************
*** 799,806 ****
                ++emsg_skip;
            CLEAR_FIELD(evalarg);
            evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
!           evalarg.eval_cookie = eap->getline == getsourceline
!                                                         ? eap->cookie : NULL;
            i = eval0(expr, &rettv, eap, &evalarg);
            if (eap->skip)
                --emsg_skip;
--- 799,809 ----
                ++emsg_skip;
            CLEAR_FIELD(evalarg);
            evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
!           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;
*** ../vim-8.2.1078/src/ex_docmd.c      2020-06-26 19:44:02.972305916 +0200
--- src/ex_docmd.c      2020-06-28 15:43:31.307673246 +0200
***************
*** 629,634 ****
--- 629,635 ----
      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,877 ****
            }
  # 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().
--- 852,857 ----
***************
*** 929,949 ****
  
  #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
         * :endwhile/:endfor.
         */
!       if (current_line == lines_ga.ga_len
!               && (cstack.cs_looplevel || has_loop_cmd(next_cmdline)))
        {
!           if (store_loop_line(&lines_ga, next_cmdline) == FAIL)
            {
                retval = FAIL;
                break;
            }
        }
        did_endif = FALSE;
  #endif
  
--- 909,952 ----
  
  #ifdef FEAT_EVAL
        /*
!        * 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 ((cstack.cs_looplevel > 0 || has_loop_cmd(next_cmdline)))
        {
!           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,1084 ****
            else if (cstack.cs_lflags & CSL_HAD_LOOP)
            {
                cstack.cs_lflags &= ~CSL_HAD_LOOP;
!               cstack.cs_line[cstack.cs_idx] = current_line - 1;
            }
        }
  
--- 1081,1087 ----
            else if (cstack.cs_lflags & CSL_HAD_LOOP)
            {
                cstack.cs_lflags &= ~CSL_HAD_LOOP;
!               cstack.cs_line[cstack.cs_idx] = current_line_before;
            }
        }
  
***************
*** 1515,1521 ****
  {
  #ifdef FEAT_EVAL
      char_u            *(*gp)(int, void *, int, int);
!     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
--- 1518,1524 ----
  {
  #ifdef FEAT_EVAL
      char_u            *(*gp)(int, void *, int, int);
!     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,1538 ****
--- 1536,1576 ----
  #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",
*** ../vim-8.2.1078/src/proto/ex_docmd.pro      2020-05-01 15:44:24.535895262 
+0200
--- src/proto/ex_docmd.pro      2020-06-28 14:44:00.208488648 +0200
***************
*** 4,13 ****
  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);
  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);
  int modifier_len(char_u *cmd);
  int cmd_exists(char_u *name);
  cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);
--- 4,14 ----
  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);
  int modifier_len(char_u *cmd);
  int cmd_exists(char_u *name);
  cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);
*** ../vim-8.2.1078/src/testdir/test_vim9_cmd.vim       2020-06-27 
23:07:31.959995377 +0200
--- src/testdir/test_vim9_cmd.vim       2020-06-28 15:45:47.707123048 +0200
***************
*** 131,142 ****
  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
        endwhile
        assert_equal(16, nr)
    END
--- 131,159 ----
  enddef
  
  def Test_while_linebreak()
    let lines =<< trim END
        vim9script
        let nr = 0
!       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
*** ../vim-8.2.1078/src/version.c       2020-06-28 13:17:07.551811006 +0200
--- src/version.c       2020-06-28 13:24:33.984114947 +0200
***************
*** 756,757 ****
--- 756,759 ----
  {   /* Add new patch number below this line */
+ /**/
+     1079,
  /**/

-- 
TERRY GILLIAM PLAYED: PATSY (ARTHUR'S TRUSTY STEED), THE GREEN KNIGHT
                      SOOTHSAYER, BRIDGEKEEPER, SIR GAWAIN (THE FIRST TO BE
                      KILLED BY THE RABBIT)
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202006281351.05SDpijC352895%40masaka.moolenaar.net.

Raspunde prin e-mail lui