Patch 7.4.2044
Problem:    filter() and map() either require a string or defining a function.
Solution:   Support lambda, a short way to define a function that evaluates an
            expression. (Yasuhiro Matsumoto, Ken Takata)
Files:      runtime/doc/eval.txt, src/eval.c, src/testdir/test_alot.vim,
            src/Makefile, src/testdir/test_channel.vim,
            src/testdir/test_lambda.vim


*** ../vim-7.4.2043/runtime/doc/eval.txt        2016-07-09 18:49:47.194420065 
+0200
--- runtime/doc/eval.txt        2016-07-15 20:30:03.024729399 +0200
***************
*** 138,146 ****
  
  1.2 Function references ~
                                        *Funcref* *E695* *E718*
! A Funcref variable is obtained with the |function()| function.        It can 
be used
! in an expression in the place of a function name, before the parenthesis
! around the arguments, to invoke the function it refers to.  Example: >
  
        :let Fn = function("MyFunc")
        :echo Fn()
--- 140,149 ----
  
  1.2 Function references ~
                                        *Funcref* *E695* *E718*
! A Funcref variable is obtained with the |function()| function or created with
! the lambda expression |expr-lambda|.  It can be used in an expression in the
! place of a function name, before the parenthesis around the arguments, to
! invoke the function it refers to.  Example: >
  
        :let Fn = function("MyFunc")
        :echo Fn()
***************
*** 695,700 ****
--- 695,701 ----
        @r                      contents of register 'r'
        function(expr1, ...)    function call
        func{ti}on(expr1, ...)  function call with curly braces
+       {args -> expr1}         lambda expression
  
  
  ".." indicates that the operations in this level can be concatenated.
***************
*** 1198,1203 ****
--- 1209,1250 ----
  See below |functions|.
  
  
+ lambda expression                             *expr-lambda* *lambda*
+ -----------------
+ {args -> expr1}               lambda expression
+ 
+ A lambda expression creates a new unnamed function which returns the result of
+ evaluating |expr1|.  Lambda expressions are differ from |user-functions| in
+ the following ways:
+ 
+ 1. The body of the lambda expression is an |expr1| and not a sequence of |Ex|
+    commands.
+ 2. The prefix "a:" is optional for arguments.  E.g.: >
+       :let F = {arg1, arg2 -> arg1 - arg2}
+       :echo F(5, 2)
+ <     3
+ 
+ The arguments are optional.  Example: >
+       :let F = {-> 'error function'}
+       :echo F()
+ <     error function
+ 
+ Examples for using a lambda expression with |sort()|, |map()| and |filter()|: 
>
+       :echo map([1, 2, 3], {idx, val -> val + 1})
+ <     [2, 3, 4] >
+       :echo sort([3,7,2,1,4], {a, b -> a - b})
+ <     [1, 2, 3, 4, 7]
+ 
+ The lambda expression is also useful for Channel, Job and timer: >
+       :let timer = timer_start(500,
+                       \ {-> execute("echo 'Handler called'", "")},
+                       \ {'repeat': 3})
+ <     Handler called
+       Handler called
+       Handler called
+ 
+ Note how execute() is used to execute an Ex command.  That's ugly though.
+ 
  ==============================================================================
  3. Internal variable                          *internal-variables* *E461*
  
*** ../vim-7.4.2043/src/eval.c  2016-07-15 20:14:40.894044219 +0200
--- src/eval.c  2016-07-15 21:14:55.401896004 +0200
***************
*** 457,462 ****
--- 457,464 ----
  static long dict_len(dict_T *d);
  static char_u *dict2string(typval_T *tv, int copyID, int restore_copyID);
  static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate);
+ static int get_function_args(char_u **argp, char_u endchar, garray_T 
*newargs, int *varargs, int skip);
+ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate);
  static char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u 
*numbuf, int copyID, int echo_style, int restore_copyID, int dict_val);
  static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int 
copyID);
  static char_u *string_quote(char_u *str, int function);
***************
*** 5261,5269 ****
                break;
  
      /*
       * Dictionary: {key: val, key: val}
       */
!     case '{': ret = get_dict_tv(arg, rettv, evaluate);
                break;
  
      /*
--- 5263,5274 ----
                break;
  
      /*
+      * Lambda: {arg, arg -> expr}
       * Dictionary: {key: val, key: val}
       */
!     case '{': ret = get_lambda_tv(arg, rettv, evaluate);
!               if (ret == NOTDONE)
!                   ret = get_dict_tv(arg, rettv, evaluate);
                break;
  
      /*
***************
*** 8110,8115 ****
--- 8115,8316 ----
      return OK;
  }
  
+ /* Get function arguments. */
+     static int
+ get_function_args(
+     char_u    **argp,
+     char_u    endchar,
+     garray_T  *newargs,
+     int               *varargs,
+     int               skip)
+ {
+     int               mustend = FALSE;
+     char_u    *arg = *argp;
+     char_u    *p = arg;
+     int               c;
+     int               i;
+ 
+     if (newargs != NULL)
+       ga_init2(newargs, (int)sizeof(char_u *), 3);
+ 
+     if (varargs != NULL)
+       *varargs = FALSE;
+ 
+     /*
+      * Isolate the arguments: "arg1, arg2, ...)"
+      */
+     while (*p != endchar)
+     {
+       if (p[0] == '.' && p[1] == '.' && p[2] == '.')
+       {
+           if (varargs != NULL)
+               *varargs = TRUE;
+           p += 3;
+           mustend = TRUE;
+       }
+       else
+       {
+           arg = p;
+           while (ASCII_ISALNUM(*p) || *p == '_')
+               ++p;
+           if (arg == p || isdigit(*arg)
+                   || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0)
+                   || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0))
+           {
+               if (!skip)
+                   EMSG2(_("E125: Illegal argument: %s"), arg);
+               break;
+           }
+           if (newargs != NULL && ga_grow(newargs, 1) == FAIL)
+               return FAIL;
+           if (newargs != NULL)
+           {
+               c = *p;
+               *p = NUL;
+               arg = vim_strsave(arg);
+               if (arg == NULL)
+                   goto err_ret;
+ 
+               /* Check for duplicate argument name. */
+               for (i = 0; i < newargs->ga_len; ++i)
+                   if (STRCMP(((char_u **)(newargs->ga_data))[i], arg) == 0)
+                   {
+                       EMSG2(_("E853: Duplicate argument name: %s"), arg);
+                       vim_free(arg);
+                       goto err_ret;
+                   }
+               ((char_u **)(newargs->ga_data))[newargs->ga_len] = arg;
+               newargs->ga_len++;
+ 
+               *p = c;
+           }
+           if (*p == ',')
+               ++p;
+           else
+               mustend = TRUE;
+       }
+       p = skipwhite(p);
+       if (mustend && *p != endchar)
+       {
+           if (!skip)
+               EMSG2(_(e_invarg2), *argp);
+           break;
+       }
+     }
+     ++p;      /* skip the ')' */
+ 
+     *argp = p;
+     return OK;
+ 
+ err_ret:
+     if (newargs != NULL)
+       ga_clear_strings(newargs);
+     return FAIL;
+ }
+ 
+ /*
+  * Parse a lambda expression and get a Funcref from "*arg".
+  * Return OK or FAIL.  Returns NOTDONE for dict or {expr}.
+  */
+     static int
+ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
+ {
+     garray_T  newargs;
+     garray_T  newlines;
+     ufunc_T   *fp = NULL;
+     int               varargs;
+     int               ret;
+     char_u    name[20];
+     char_u    *start = skipwhite(*arg + 1);
+     char_u    *s, *e;
+     static int        lambda_no = 0;
+ 
+     ga_init(&newargs);
+     ga_init(&newlines);
+ 
+     /* First, check if this is a lambda expression. "->" must exists. */
+     ret = get_function_args(&start, '-', NULL, NULL, TRUE);
+     if (ret == FAIL || *start != '>')
+       return NOTDONE;
+ 
+     /* Parse the arguments again. */
+     *arg = skipwhite(*arg + 1);
+     ret = get_function_args(arg, '-', &newargs, &varargs, FALSE);
+     if (ret == FAIL || **arg != '>')
+       goto errret;
+ 
+     /* Get the start and the end of the expression. */
+     *arg = skipwhite(*arg + 1);
+     s = *arg;
+     ret = skip_expr(arg);
+     if (ret == FAIL)
+       goto errret;
+     e = *arg;
+     *arg = skipwhite(*arg);
+     if (**arg != '}')
+       goto errret;
+     ++*arg;
+ 
+     if (evaluate)
+     {
+       int     len;
+       char_u  *p;
+ 
+       fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + 20));
+       if (fp == NULL)
+           goto errret;
+ 
+       sprintf((char*)name, "<lambda>%d", ++lambda_no);
+ 
+       ga_init2(&newlines, (int)sizeof(char_u *), 1);
+       if (ga_grow(&newlines, 1) == FAIL)
+           goto errret;
+ 
+       /* Add "return " before the expression.
+        * TODO: Support multiple expressions.  */
+       len = 7 + e - s + 1;
+       p = (char_u *)alloc(len);
+       if (p == NULL)
+           goto errret;
+       ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
+       STRCPY(p, "return ");
+       STRNCPY(p + 7, s, e - s);
+       p[7 + e - s] = NUL;
+ 
+       fp->uf_refcount = 1;
+       STRCPY(fp->uf_name, name);
+       hash_add(&func_hashtab, UF2HIKEY(fp));
+       fp->uf_args = newargs;
+       fp->uf_lines = newlines;
+ 
+ #ifdef FEAT_PROFILE
+       fp->uf_tml_count = NULL;
+       fp->uf_tml_total = NULL;
+       fp->uf_tml_self = NULL;
+       fp->uf_profiling = FALSE;
+       if (prof_def_func())
+           func_do_profile(fp);
+ #endif
+       fp->uf_varargs = TRUE;
+       fp->uf_flags = 0;
+       fp->uf_calls = 0;
+       fp->uf_script_ID = current_SID;
+ 
+       rettv->vval.v_string = vim_strsave(name);
+       rettv->v_type = VAR_FUNC;
+     }
+     else
+       ga_clear_strings(&newargs);
+ 
+     return OK;
+ 
+ errret:
+     ga_clear_strings(&newargs);
+     ga_clear_strings(&newlines);
+     vim_free(fp);
+     return FAIL;
+ }
+ 
      static char *
  get_var_special_name(int nr)
  {
***************
*** 9321,9327 ****
                    call_user_func(fp, argcount, argvars, rettv,
                                               firstline, lastline,
                                  (fp->uf_flags & FC_DICT) ? selfdict : NULL);
!                   if (--fp->uf_calls <= 0 && isdigit(*fp->uf_name)
                                                      && fp->uf_refcount <= 0)
                        /* Function was unreferenced while being used, free it
                         * now. */
--- 9522,9529 ----
                    call_user_func(fp, argcount, argvars, rettv,
                                               firstline, lastline,
                                  (fp->uf_flags & FC_DICT) ? selfdict : NULL);
!                   if (--fp->uf_calls <= 0 && (isdigit(*fp->uf_name)
!                               || STRNCMP(fp->uf_name, "<lambda>", 8) == 0)
                                                      && fp->uf_refcount <= 0)
                        /* Function was unreferenced while being used, free it
                         * now. */
***************
*** 24275,24281 ****
  ex_function(exarg_T *eap)
  {
      char_u    *theline;
-     int               i;
      int               j;
      int               c;
      int               saved_did_emsg;
--- 24477,24482 ----
***************
*** 24287,24293 ****
      garray_T  newargs;
      garray_T  newlines;
      int               varargs = FALSE;
-     int               mustend = FALSE;
      int               flags = 0;
      ufunc_T   *fp;
      int               indent;
--- 24488,24493 ----
***************
*** 24468,24474 ****
      }
      p = skipwhite(p + 1);
  
-     ga_init2(&newargs, (int)sizeof(char_u *), 3);
      ga_init2(&newlines, (int)sizeof(char_u *), 3);
  
      if (!eap->skip)
--- 24668,24673 ----
***************
*** 24498,24563 ****
            EMSG(_("E862: Cannot use g: here"));
      }
  
!     /*
!      * Isolate the arguments: "arg1, arg2, ...)"
!      */
!     while (*p != ')')
!     {
!       if (p[0] == '.' && p[1] == '.' && p[2] == '.')
!       {
!           varargs = TRUE;
!           p += 3;
!           mustend = TRUE;
!       }
!       else
!       {
!           arg = p;
!           while (ASCII_ISALNUM(*p) || *p == '_')
!               ++p;
!           if (arg == p || isdigit(*arg)
!                   || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0)
!                   || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0))
!           {
!               if (!eap->skip)
!                   EMSG2(_("E125: Illegal argument: %s"), arg);
!               break;
!           }
!           if (ga_grow(&newargs, 1) == FAIL)
!               goto erret;
!           c = *p;
!           *p = NUL;
!           arg = vim_strsave(arg);
!           if (arg == NULL)
!               goto erret;
! 
!           /* Check for duplicate argument name. */
!           for (i = 0; i < newargs.ga_len; ++i)
!               if (STRCMP(((char_u **)(newargs.ga_data))[i], arg) == 0)
!               {
!                   EMSG2(_("E853: Duplicate argument name: %s"), arg);
!                   vim_free(arg);
!                   goto erret;
!               }
! 
!           ((char_u **)(newargs.ga_data))[newargs.ga_len] = arg;
!           *p = c;
!           newargs.ga_len++;
!           if (*p == ',')
!               ++p;
!           else
!               mustend = TRUE;
!       }
!       p = skipwhite(p);
!       if (mustend && *p != ')')
!       {
!           if (!eap->skip)
!               EMSG2(_(e_invarg2), eap->arg);
!           break;
!       }
!     }
!     if (*p != ')')
!       goto erret;
!     ++p;      /* skip the ')' */
  
      /* find extra arguments "range", "dict" and "abort" */
      for (;;)
--- 24697,24704 ----
            EMSG(_("E862: Cannot use g: here"));
      }
  
!     if (get_function_args(&p, ')', &newargs, &varargs, eap->skip) == FAIL)
!       goto errret_2;
  
      /* find extra arguments "range", "dict" and "abort" */
      for (;;)
***************
*** 24926,24931 ****
--- 25067,25073 ----
  
  erret:
      ga_clear_strings(&newargs);
+ errret_2:
      ga_clear_strings(&newlines);
  ret_free:
      vim_free(skip_until);
***************
*** 25740,25746 ****
  {
      ufunc_T *fp;
  
!     if (name != NULL && isdigit(*name))
      {
        fp = find_func(name);
        if (fp == NULL)
--- 25882,25890 ----
  {
      ufunc_T *fp;
  
!     if (name == NULL)
!       return;
!     else if (isdigit(*name))
      {
        fp = find_func(name);
        if (fp == NULL)
***************
*** 25758,25763 ****
--- 25902,25919 ----
                func_free(fp);
        }
      }
+     else if (STRNCMP(name, "<lambda>", 8) == 0)
+     {
+       /* fail silently, when lambda function isn't found. */
+       fp = find_func(name);
+       if (fp != NULL && --fp->uf_refcount <= 0)
+       {
+           /* Only delete it when it's not being used.  Otherwise it's done
+            * when "uf_calls" becomes zero. */
+           if (fp->uf_calls == 0)
+               func_free(fp);
+       }
+     }
  }
  
  /*
***************
*** 25768,25774 ****
  {
      ufunc_T *fp;
  
!     if (name != NULL && isdigit(*name))
      {
        fp = find_func(name);
        if (fp == NULL)
--- 25924,25932 ----
  {
      ufunc_T *fp;
  
!     if (name == NULL)
!       return;
!     else if (isdigit(*name))
      {
        fp = find_func(name);
        if (fp == NULL)
***************
*** 25776,25781 ****
--- 25934,25946 ----
        else
            ++fp->uf_refcount;
      }
+     else if (STRNCMP(name, "<lambda>", 8) == 0)
+     {
+       /* fail silently, when lambda function isn't found. */
+       fp = find_func(name);
+       if (fp != NULL)
+           ++fp->uf_refcount;
+     }
  }
  
  /*
***************
*** 25801,25806 ****
--- 25966,25972 ----
      int               fixvar_idx = 0; /* index in fixvar[] */
      int               i;
      int               ai;
+     int               islambda = FALSE;
      char_u    numbuf[NUMBUFLEN];
      char_u    *name;
      size_t    len;
***************
*** 25834,25839 ****
--- 26000,26008 ----
      fc->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0);
      fc->dbg_tick = debug_tick;
  
+     if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0)
+       islambda = TRUE;
+ 
      /*
       * Note about using fc->fixvar[]: This is an array of FIXVAR_CNT variables
       * with names up to VAR_SHORT_LEN long.  This avoids having to alloc/free
***************
*** 25891,25900 ****
--- 26060,26076 ----
                                                       (varnumber_T)lastline);
      for (i = 0; i < argcount; ++i)
      {
+       int         addlocal = FALSE;
+       dictitem_T  *v2;
+ 
        ai = i - fp->uf_args.ga_len;
        if (ai < 0)
+       {
            /* named argument a:name */
            name = FUNCARG(fp, i);
+           if (islambda)
+               addlocal = TRUE;
+       }
        else
        {
            /* "..." argument a:1, a:2, etc. */
***************
*** 25905,25910 ****
--- 26081,26089 ----
        {
            v = &fc->fixvar[fixvar_idx++].var;
            v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
+ 
+           if (addlocal)
+               v2 = v;
        }
        else
        {
***************
*** 25913,25918 ****
--- 26092,26109 ----
            if (v == NULL)
                break;
            v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
+ 
+           if (addlocal)
+           {
+               v2 = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T)
+                                                            + STRLEN(name)));
+               if (v2 == NULL)
+               {
+                   vim_free(v);
+                   break;
+               }
+               v2->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
+           }
        }
        STRCPY(v->di_key, name);
        hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
***************
*** 25922,25927 ****
--- 26113,26128 ----
        v->di_tv = argvars[i];
        v->di_tv.v_lock = VAR_FIXED;
  
+       /* Named arguments can be accessed without the "a:" prefix in lambda
+        * expressions.  Add to the l: dict. */
+       if (addlocal)
+       {
+           STRCPY(v2->di_key, name);
+           copy_tv(&v->di_tv, &v2->di_tv);
+           v2->di_tv.v_lock = VAR_FIXED;
+           hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v2));
+       }
+ 
        if (ai >= 0 && ai < MAX_FUNC_ARGS)
        {
            list_append(&fc->l_varlist, &fc->l_listitems[ai]);
*** ../vim-7.4.2043/src/testdir/test_alot.vim   2016-07-09 17:05:49.207222368 
+0200
--- src/testdir/test_alot.vim   2016-07-15 20:19:43.485674224 +0200
***************
*** 19,24 ****
--- 19,25 ----
  source test_help_tagjump.vim
  source test_join.vim
  source test_jumps.vim
+ source test_lambda.vim
  source test_lispwords.vim
  source test_matchstrpos.vim
  source test_menu.vim
*** ../vim-7.4.2043/src/Makefile        2016-07-15 17:08:45.662711053 +0200
--- src/Makefile        2016-07-15 20:20:49.720717710 +0200
***************
*** 2046,2051 ****
--- 2046,2052 ----
        test_join \
        test_json \
        test_jumps \
+       test_lambda \
        test_langmap \
        test_largefile \
        test_lispwords \
*** ../vim-7.4.2043/src/testdir/test_channel.vim        2016-07-15 
17:08:45.662711053 +0200
--- src/testdir/test_channel.vim        2016-07-15 20:57:27.201006106 +0200
***************
*** 95,100 ****
--- 95,112 ----
    endif
    call assert_equal('got it', g:Ch_responseMsg)
  
+   " Using lambda.
+   let g:Ch_responseMsg = ''
+   call ch_sendexpr(handle, 'hello!', {'callback': {a, b -> 
Ch_requestHandler(a, b)}})
+   call WaitFor('exists("g:Ch_responseHandle")')
+   if !exists('g:Ch_responseHandle')
+     call assert_false(1, 'g:Ch_responseHandle was not set')
+   else
+     call assert_equal(handle, g:Ch_responseHandle)
+     unlet g:Ch_responseHandle
+   endif
+   call assert_equal('got it', g:Ch_responseMsg)
+ 
    " Collect garbage, tests that our handle isn't collected.
    call test_garbagecollect_now()
  
***************
*** 1069,1074 ****
--- 1081,1112 ----
    endtry
  endfunc
  
+ func Test_out_cb_lambda()
+   if !has('job')
+     return
+   endif
+   call ch_log('Test_out_cb_lambda()')
+ 
+   let job = job_start(s:python . " test_channel_pipe.py",
+   \ {'out_cb': {ch, msg -> execute("let g:Ch_outmsg = 'lambda: ' . msg")},
+   \ 'out_mode': 'json',
+   \ 'err_cb': {ch, msg -> execute(":let g:Ch_errmsg = 'lambda: ' . msg")},
+   \ 'err_mode': 'json'})
+   call assert_equal("run", job_status(job))
+   try
+     let g:Ch_outmsg = ''
+     let g:Ch_errmsg = ''
+     call ch_sendraw(job, "echo [0, \"hello\"]\n")
+     call ch_sendraw(job, "echoerr [0, \"there\"]\n")
+     call WaitFor('g:Ch_outmsg != ""')
+     call assert_equal("lambda: hello", g:Ch_outmsg)
+     call WaitFor('g:Ch_errmsg != ""')
+     call assert_equal("lambda: there", g:Ch_errmsg)
+   finally
+     call job_stop(job)
+   endtry
+ endfunc
+ 
  """"""""""
  
  let g:Ch_unletResponse = ''
***************
*** 1285,1290 ****
--- 1323,1346 ----
    bwipe!
  endfunc
  
+ function Ch_test_close_lambda(port)
+   let handle = ch_open('localhost:' . a:port, s:chopt)
+   if ch_status(handle) == "fail"
+     call assert_false(1, "Can't open channel")
+     return
+   endif
+   let g:Ch_close_ret = ''
+   call ch_setoptions(handle, {'close_cb': {ch -> execute("let g:Ch_close_ret 
= 'closed'")}})
+ 
+   call assert_equal('', ch_evalexpr(handle, 'close me'))
+   call WaitFor('"closed" == g:Ch_close_ret')
+   call assert_equal('closed', g:Ch_close_ret)
+ endfunc
+ 
+ func Test_close_lambda()
+   call ch_log('Test_close_lambda()')
+   call s:run_server('Ch_test_close_lambda')
+ endfunc
  
  " Uncomment this to see what happens, output is in src/testdir/channellog.
   call ch_logfile('channellog', 'w')
*** ../vim-7.4.2043/src/testdir/test_lambda.vim 2016-07-15 21:21:55.431842515 
+0200
--- src/testdir/test_lambda.vim 2016-07-15 21:16:45.008316561 +0200
***************
*** 0 ****
--- 1,48 ----
+ function! Test_lambda_with_filter()
+   let s:x = 2
+   call assert_equal([2, 3], filter([1, 2, 3], {i, v -> v >= s:x}))
+ endfunction
+ 
+ function! Test_lambda_with_map()
+   let s:x = 1
+   call assert_equal([2, 3, 4], map([1, 2, 3], {i, v -> v + s:x}))
+ endfunction
+ 
+ function! Test_lambda_with_sort()
+   call assert_equal([1, 2, 3, 4, 7], sort([3,7,2,1,4], {a, b -> a - b}))
+ endfunction
+ 
+ function! Test_lambda_with_timer()
+   if !has('timers')
+     return
+   endif
+ 
+   let s:n = 0
+   let s:timer_id = 0
+   function! s:Foo()
+     "let n = 0
+     let s:timer_id = timer_start(50, {-> execute("let s:n += 1 | echo s:n")}, 
{"repeat": -1})
+   endfunction
+ 
+   call s:Foo()
+   sleep 200ms
+   " do not collect lambda
+   call test_garbagecollect_now()
+   let m = s:n
+   sleep 200ms
+   call timer_stop(s:timer_id)
+   call assert_true(m > 1)
+   call assert_true(s:n > m + 1)
+   call assert_true(s:n < 9)
+ endfunction
+ 
+ function! Test_lambda_with_partial()
+   let l:Cb = function({... -> ['zero', a:1, a:2, a:3]}, ['one', 'two'])
+   call assert_equal(['zero', 'one', 'two', 'three'], l:Cb('three'))
+ endfunction
+ 
+ function Test_lambda_fails()
+   call assert_equal(3, {a, b -> a + b}(1, 2))
+   call assert_fails('echo {a, a -> a + a}(1, 2)', 'E15:')
+   call assert_fails('echo {a, b -> a + b)}(1, 2)', 'E15:')
+ endfunc
*** ../vim-7.4.2043/src/version.c       2016-07-15 20:14:40.894044219 +0200
--- src/version.c       2016-07-15 21:22:05.411698629 +0200
***************
*** 760,761 ****
--- 760,763 ----
  {   /* Add new patch number below this line */
+ /**/
+     2044,
  /**/

-- 
Back up my hard drive?  I can't find the reverse switch!

 /// 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].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui