Patch 8.2.1155
Problem:    Vim9: cannot handle line break inside lambda.
Solution:   Pass the compilation context through. (closes #6407, closes #6409)
Files:      src/structs.h, src/vim9compile.c, src/proto/vim9compile.pro,
            src/eval.c, src/testdir/test_vim9_func.vim


*** ../vim-8.2.1154/src/structs.h       2020-07-08 15:16:15.534128895 +0200
--- src/structs.h       2020-07-08 15:25:39.568322556 +0200
***************
*** 1765,1770 ****
--- 1765,1773 ----
      char_u    *(*eval_getline)(int, void *, int, int);
      void      *eval_cookie;       // argument for eval_getline()
  
+     // used when compiling a :def function, NULL otherwise
+     cctx_T    *eval_cctx;
+ 
      // Used to collect lines while parsing them, so that they can be
      // concatenated later.  Used when "eval_ga.ga_itemsize" is not zero.
      // "eval_ga.ga_data" is a list of pointers to lines.
*** ../vim-8.2.1154/src/vim9compile.c   2020-07-07 22:50:08.831459969 +0200
--- src/vim9compile.c   2020-07-08 17:13:23.770249096 +0200
***************
*** 2397,2404 ****
   * comment. Skips over white space.
   * Returns NULL if there is none.
   */
!     static char_u *
! peek_next_line(cctx_T *cctx)
  {
      int lnum = cctx->ctx_lnum;
  
--- 2397,2404 ----
   * comment. Skips over white space.
   * Returns NULL if there is none.
   */
!     char_u *
! peek_next_line_from_context(cctx_T *cctx)
  {
      int lnum = cctx->ctx_lnum;
  
***************
*** 2430,2436 ****
      *nextp = NULL;
      if (*p == NUL || (VIM_ISWHITE(*arg) && comment_start(p)))
      {
!       *nextp = peek_next_line(cctx);
        if (*nextp != NULL)
            return *nextp;
      }
--- 2430,2436 ----
      *nextp = NULL;
      if (*p == NUL || (VIM_ISWHITE(*arg) && comment_start(p)))
      {
!       *nextp = peek_next_line_from_context(cctx);
        if (*nextp != NULL)
            return *nextp;
      }
***************
*** 2442,2448 ****
   * Skips over empty lines.  Skips over comment lines if "skip_comment" is 
TRUE.
   * Returns NULL when at the end.
   */
!     static char_u *
  next_line_from_context(cctx_T *cctx, int skip_comment)
  {
      char_u    *line;
--- 2442,2448 ----
   * Skips over empty lines.  Skips over comment lines if "skip_comment" is 
TRUE.
   * Returns NULL when at the end.
   */
!     char_u *
  next_line_from_context(cctx_T *cctx, int skip_comment)
  {
      char_u    *line;
***************
*** 3079,3087 ****
  {
      typval_T  rettv;
      ufunc_T   *ufunc;
  
      // Get the funcref in "rettv".
!     if (get_lambda_tv(arg, &rettv, &EVALARG_EVALUATE) != OK)
        return FAIL;
  
      ufunc = rettv.vval.v_partial->pt_func;
--- 3079,3092 ----
  {
      typval_T  rettv;
      ufunc_T   *ufunc;
+     evalarg_T evalarg;
+ 
+     CLEAR_FIELD(evalarg);
+     evalarg.eval_flags = EVAL_EVALUATE;
+     evalarg.eval_cctx = cctx;
  
      // Get the funcref in "rettv".
!     if (get_lambda_tv(arg, &rettv, &evalarg) != OK)
        return FAIL;
  
      ufunc = rettv.vval.v_partial->pt_func;
***************
*** 3535,3540 ****
--- 3540,3546 ----
  
  /*
   * Compile whatever comes after "name" or "name()".
+  * Advances "*arg" only when something was recognized.
   */
      static int
  compile_subscript(
***************
*** 3550,3556 ****
  
        if (*p == NUL || (VIM_ISWHITE(**arg) && comment_start(p)))
        {
!           char_u *next = peek_next_line(cctx);
  
            // If a following line starts with "->{" or "->X" advance to that
            // line, so that a line break before "->" is allowed.
--- 3556,3562 ----
  
        if (*p == NUL || (VIM_ISWHITE(**arg) && comment_start(p)))
        {
!           char_u *next = peek_next_line_from_context(cctx);
  
            // If a following line starts with "->{" or "->X" advance to that
            // line, so that a line break before "->" is allowed.
***************
*** 3560,3570 ****
                next = next_line_from_context(cctx, TRUE);
                if (next == NULL)
                    return FAIL;
!               *arg = skipwhite(next);
            }
        }
  
!       if (**arg == '(')
        {
            garray_T    *stack = &cctx->ctx_type_stack;
            type_T      *type;
--- 3566,3577 ----
                next = next_line_from_context(cctx, TRUE);
                if (next == NULL)
                    return FAIL;
!               *arg = next;
!               p = skipwhite(*arg);
            }
        }
  
!       if (*p == '(')
        {
            garray_T    *stack = &cctx->ctx_type_stack;
            type_T      *type;
***************
*** 3576,3588 ****
            // funcref(arg)
            type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
  
!           *arg = skipwhite(*arg + 1);
            if (compile_arguments(arg, cctx, &argcount) == FAIL)
                return FAIL;
            if (generate_PCALL(cctx, argcount, end_leader, type, TRUE) == FAIL)
                return FAIL;
        }
!       else if (**arg == '-' && (*arg)[1] == '>')
        {
            if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
--- 3583,3595 ----
            // funcref(arg)
            type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
  
!           *arg = skipwhite(p + 1);
            if (compile_arguments(arg, cctx, &argcount) == FAIL)
                return FAIL;
            if (generate_PCALL(cctx, argcount, end_leader, type, TRUE) == FAIL)
                return FAIL;
        }
!       else if (*p == '-' && p[1] == '>')
        {
            if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
***************
*** 3594,3600 ****
                return FAIL;
            *start_leader = end_leader;   // don't apply again later
  
!           p = *arg + 2;
            *arg = skipwhite(p);
            if (may_get_next_line(p, arg, cctx) == FAIL)
                return FAIL;
--- 3601,3607 ----
                return FAIL;
            *start_leader = end_leader;   // don't apply again later
  
!           p += 2;
            *arg = skipwhite(p);
            if (may_get_next_line(p, arg, cctx) == FAIL)
                return FAIL;
***************
*** 3622,3628 ****
                    return FAIL;
            }
        }
!       else if (**arg == '[')
        {
            garray_T    *stack = &cctx->ctx_type_stack;
            type_T      **typep;
--- 3629,3635 ----
                    return FAIL;
            }
        }
!       else if (*p == '[')
        {
            garray_T    *stack = &cctx->ctx_type_stack;
            type_T      **typep;
***************
*** 3635,3641 ****
            if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
  
!           p = *arg + 1;
            *arg = skipwhite(p);
            if (may_get_next_line(p, arg, cctx) == FAIL)
                return FAIL;
--- 3642,3648 ----
            if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
  
!           ++p;
            *arg = skipwhite(p);
            if (may_get_next_line(p, arg, cctx) == FAIL)
                return FAIL;
***************
*** 3671,3682 ****
                return FAIL;
            }
        }
!       else if (**arg == '.' && (*arg)[1] != '.')
        {
            if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
  
!           ++*arg;
            if (may_get_next_line(*arg, arg, cctx) == FAIL)
                return FAIL;
            // dictionary member: dict.name
--- 3678,3689 ----
                return FAIL;
            }
        }
!       else if (*p == '.' && p[1] != '.')
        {
            if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
  
!           *arg = p + 1;
            if (may_get_next_line(*arg, arg, cctx) == FAIL)
                return FAIL;
            // dictionary member: dict.name
*** ../vim-8.2.1154/src/proto/vim9compile.pro   2020-07-06 21:53:14.472719363 
+0200
--- src/proto/vim9compile.pro   2020-07-08 13:38:49.964316011 +0200
***************
*** 8,13 ****
--- 8,15 ----
  char *type_name(type_T *type, char **tofree);
  int get_script_item_idx(int sid, char_u *name, int check_writable);
  imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx);
+ char_u *peek_next_line_from_context(cctx_T *cctx);
+ char_u *next_line_from_context(cctx_T *cctx, int skip_comment);
  char_u *to_name_const_end(char_u *arg);
  int assignment_len(char_u *p, int *heredoc);
  void vim9_declare_error(char_u *name);
*** ../vim-8.2.1154/src/eval.c  2020-07-04 14:14:55.633073475 +0200
--- src/eval.c  2020-07-08 17:31:11.235141345 +0200
***************
*** 390,400 ****
      garray_T    *gap = &evalarg->eval_ga;
      int               save_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
  
!     if (vim9script && evalarg->eval_cookie != NULL)
      {
        ga_init2(gap, sizeof(char_u *), 10);
        if (ga_grow(gap, 1) == OK)
-           // leave room for "start"
            ++gap->ga_len;
      }
  
--- 390,401 ----
      garray_T    *gap = &evalarg->eval_ga;
      int               save_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
  
!     if (vim9script
!              && (evalarg->eval_cookie != NULL || evalarg->eval_cctx != NULL))
      {
        ga_init2(gap, sizeof(char_u *), 10);
+       // leave room for "start"
        if (ga_grow(gap, 1) == OK)
            ++gap->ga_len;
      }
  
***************
*** 406,437 ****
      if (evalarg != NULL)
        evalarg->eval_flags = save_flags;
  
!     if (vim9script && evalarg->eval_cookie != NULL
!                                               && evalarg->eval_ga.ga_len > 1)
      {
!       char_u      *p;
!       size_t      endoff = STRLEN(*end);
  
!       // Line breaks encountered, concatenate all the lines.
!       *((char_u **)gap->ga_data) = *start;
!       p = ga_concat_strings(gap, "");
!       *((char_u **)gap->ga_data) = NULL;
!       ga_clear_strings(gap);
!       gap->ga_itemsize = 0;
!       if (p == NULL)
!           return FAIL;
!       *start = p;
!       vim_free(evalarg->eval_tofree);
!       evalarg->eval_tofree = p;
!       // Compute "end" relative to the end.
!       *end = *start + STRLEN(*start) - endoff;
      }
  
      return res;
  }
  
  /*
!  * Top level evaluation function, returning a string.
   * When "convert" is TRUE convert a List into a sequence of lines and convert
   * a Float to a String.
   * Return pointer to allocated memory, or NULL for failure.
--- 407,455 ----
      if (evalarg != NULL)
        evalarg->eval_flags = save_flags;
  
!     if (vim9script
!           && (evalarg->eval_cookie != NULL || evalarg->eval_cctx != NULL))
      {
!       if (evalarg->eval_ga.ga_len == 1)
!       {
!           // just one line, no need to concatenate
!           ga_clear(gap);
!           gap->ga_itemsize = 0;
!       }
!       else
!       {
!           char_u          *p;
!           size_t          endoff = STRLEN(*end);
  
!           // Line breaks encountered, concatenate all the lines.
!           *((char_u **)gap->ga_data) = *start;
!           p = ga_concat_strings(gap, "");
! 
!           // free the lines only when using getsourceline()
!           if (evalarg->eval_cookie != NULL)
!           {
!               *((char_u **)gap->ga_data) = NULL;
!               ga_clear_strings(gap);
!           }
!           else
!               ga_clear(gap);
!           gap->ga_itemsize = 0;
!           if (p == NULL)
!               return FAIL;
!           *start = p;
!           vim_free(evalarg->eval_tofree);
!           evalarg->eval_tofree = p;
!           // Compute "end" relative to the end.
!           *end = *start + STRLEN(*start) - endoff;
!       }
      }
  
      return res;
  }
  
  /*
!  * Top level evaluation function, returning a string.  Does not handle line
!  * breaks.
   * When "convert" is TRUE convert a List into a sequence of lines and convert
   * a Float to a String.
   * Return pointer to allocated memory, or NULL for failure.
***************
*** 1878,1888 ****
      *getnext = FALSE;
      if (current_sctx.sc_version == SCRIPT_VERSION_VIM9
            && evalarg != NULL
!           && evalarg->eval_cookie != NULL
            && (*arg == NUL || (VIM_ISWHITE(arg[-1])
                                             && *arg == '#' && arg[1] != '{')))
      {
!       char_u *p = getline_peek(evalarg->eval_getline, evalarg->eval_cookie);
  
        if (p != NULL)
        {
--- 1896,1911 ----
      *getnext = FALSE;
      if (current_sctx.sc_version == SCRIPT_VERSION_VIM9
            && evalarg != NULL
!           && (evalarg->eval_cookie != NULL || evalarg->eval_cctx != NULL)
            && (*arg == NUL || (VIM_ISWHITE(arg[-1])
                                             && *arg == '#' && arg[1] != '{')))
      {
!       char_u *p;
! 
!       if (evalarg->eval_cookie != NULL)
!           p = getline_peek(evalarg->eval_getline, evalarg->eval_cookie);
!       else
!           p = peek_next_line_from_context(evalarg->eval_cctx);
  
        if (p != NULL)
        {
***************
*** 1902,1908 ****
      garray_T  *gap = &evalarg->eval_ga;
      char_u    *line;
  
!     line = evalarg->eval_getline(0, evalarg->eval_cookie, 0, TRUE);
      ++evalarg->eval_break_count;
      if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK)
      {
--- 1925,1934 ----
      garray_T  *gap = &evalarg->eval_ga;
      char_u    *line;
  
!     if (evalarg->eval_cookie != NULL)
!       line = evalarg->eval_getline(0, evalarg->eval_cookie, 0, TRUE);
!     else
!       line = next_line_from_context(evalarg->eval_cctx, TRUE);
      ++evalarg->eval_break_count;
      if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK)
      {
***************
*** 5034,5068 ****
      int               ret = OK;
      dict_T    *selfdict = NULL;
      int               check_white = TRUE;
  
!     // When at the end of the line and ".name" follows in the next line then
!     // consume the line break.  Only when rettv is a dict.
!     if (rettv->v_type == VAR_DICT)
      {
!       int     getnext;
!       char_u  *p = eval_next_non_blank(*arg, evalarg, &getnext);
! 
!       if (getnext && *p == '.' && ASCII_ISALPHA(p[1]))
        {
            *arg = eval_next_line(evalarg);
            check_white = FALSE;
        }
-     }
  
!     // "." is ".name" lookup when we found a dict or when evaluating and
!     // scriptversion is at least 2, where string concatenation is "..".
!     while (ret == OK
!           && (((**arg == '['
!                   || (**arg == '.' && (rettv->v_type == VAR_DICT
!                       || (!evaluate
!                           && (*arg)[1] != '.'
!                           && current_sctx.sc_version >= 2)))
!                   || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC
!                                           || rettv->v_type == VAR_PARTIAL)))
!               && (!check_white || !VIM_ISWHITE(*(*arg - 1))))
!           || (**arg == '-' && (*arg)[1] == '>')))
!     {
!       if (**arg == '(')
        {
            ret = call_func_rettv(arg, evalarg, rettv, evaluate,
                                                               selfdict, NULL);
--- 5060,5086 ----
      int               ret = OK;
      dict_T    *selfdict = NULL;
      int               check_white = TRUE;
+     int               getnext;
+     char_u    *p;
  
!     while (ret == OK)
      {
!       // When at the end of the line and ".name" or "->{" or "->X" follows in
!       // the next line then consume the line break.
!       p = eval_next_non_blank(*arg, evalarg, &getnext);
!       if (getnext
!           && ((rettv->v_type == VAR_DICT && *p == '.'
!                                                      && ASCII_ISALPHA(p[1]))
!               || (*p == '-' && p[1] == '>'
!                                    && (p[2] == '{' || ASCII_ISALPHA(p[2])))))
        {
            *arg = eval_next_line(evalarg);
            check_white = FALSE;
        }
  
!       if ((**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC
!                           || rettv->v_type == VAR_PARTIAL))
!                   && (!check_white || !VIM_ISWHITE(*(*arg - 1))))
        {
            ret = call_func_rettv(arg, evalarg, rettv, evaluate,
                                                               selfdict, NULL);
***************
*** 5079,5085 ****
            dict_unref(selfdict);
            selfdict = NULL;
        }
!       else if (**arg == '-')
        {
            if (ret == OK)
            {
--- 5097,5103 ----
            dict_unref(selfdict);
            selfdict = NULL;
        }
!       else if (**arg == '-' && (*arg)[1] == '>')
        {
            if (ret == OK)
            {
***************
*** 5091,5097 ****
                    ret = eval_method(arg, rettv, evalarg, verbose);
            }
        }
!       else // **arg == '[' || **arg == '.'
        {
            dict_unref(selfdict);
            if (rettv->v_type == VAR_DICT)
--- 5109,5121 ----
                    ret = eval_method(arg, rettv, evalarg, verbose);
            }
        }
!       // "." is ".name" lookup when we found a dict or when evaluating and
!       // scriptversion is at least 2, where string concatenation is "..".
!       else if (**arg == '['
!               || (**arg == '.' && (rettv->v_type == VAR_DICT
!                       || (!evaluate
!                           && (*arg)[1] != '.'
!                           && current_sctx.sc_version >= 2))))
        {
            dict_unref(selfdict);
            if (rettv->v_type == VAR_DICT)
***************
*** 5108,5113 ****
--- 5132,5139 ----
                ret = FAIL;
            }
        }
+       else
+           break;
      }
  
      // Turn "dict.Func" into a partial for "Func" bound to "dict".
*** ../vim-8.2.1154/src/testdir/test_vim9_func.vim      2020-07-05 
21:10:20.869634742 +0200
--- src/testdir/test_vim9_func.vim      2020-07-08 16:51:18.386260987 +0200
***************
*** 965,970 ****
--- 965,982 ----
    assert_equal('full', Line_continuation_in_def('.'))
  enddef
  
+ def Line_continuation_in_lambda(): list<number>
+   let x = range(97, 100)
+       ->map({_,v -> nr2char(v)
+           ->toupper()})
+       ->reverse()
+   return x
+ enddef
+ 
+ def Test_line_continuation_in_lambda()
+   assert_equal(['D', 'C', 'B', 'A'], Line_continuation_in_lambda())
+ enddef
+ 
  func Test_silent_echo()
    CheckScreendump
  
*** ../vim-8.2.1154/src/version.c       2020-07-08 15:16:15.534128895 +0200
--- src/version.c       2020-07-08 17:35:35.026243838 +0200
***************
*** 756,757 ****
--- 756,759 ----
  {   /* Add new patch number below this line */
+ /**/
+     1155,
  /**/

-- 
For society, it's probably a good thing that engineers value function over
appearance.  For example, you wouldn't want engineers to build nuclear power
plants that only _look_ like they would keep all the radiation inside.
                                (Scott Adams - The Dilbert principle)

 /// 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/202007081536.068FarLo1375015%40masaka.moolenaar.net.

Raspunde prin e-mail lui