Patch 7.4.1989
Problem:    filter() and map() only accept a string argument.
Solution:   Implement using a Funcref argument (Yasuhiro Matsumoto, Ken
            Takata)
Files:      runtime/doc/eval.txt, src/Makefile, src/eval.c,
            src/testdir/test_alot.vim, src/testdir/test_filter_map.vim,
            src/testdir/test_partial.vim


*** ../vim-7.4.1988/runtime/doc/eval.txt        2016-07-01 18:16:47.481936426 
+0200
--- runtime/doc/eval.txt        2016-07-04 22:16:37.435329754 +0200
***************
*** 3478,3508 ****
                directory, and we can write to it, the result is 2.
  
  
! filter({expr}, {string})                                      *filter()*
!               {expr} must be a |List| or a |Dictionary|.
!               For each item in {expr} evaluate {string} and when the result
                is zero remove the item from the |List| or |Dictionary|.
!               Inside {string} |v:val| has the value of the current item.
!               For a |Dictionary| |v:key| has the key of the current item.
                Examples: >
!                       :call filter(mylist, 'v:val !~ "OLD"')
  <             Removes the items where "OLD" appears. >
!                       :call filter(mydict, 'v:key >= 8')
  <             Removes the items with a key below 8. >
!                       :call filter(var, 0)
  <             Removes all the items, thus clears the |List| or |Dictionary|.
  
!               Note that {string} is the result of expression and is then
                used as an expression again.  Often it is good to use a
                |literal-string| to avoid having to double backslashes.
  
                The operation is done in-place.  If you want a |List| or
                |Dictionary| to remain unmodified make a copy first: >
                        :let l = filter(copy(mylist), 'v:val =~ "KEEP"')
  
! <             Returns {expr}, the |List| or |Dictionary| that was filtered.
!               When an error is encountered while evaluating {string} no
!               further items in {expr} are processed.
  
  
  finddir({name}[, {path}[, {count}]])                          *finddir()*
--- 3521,3566 ----
                directory, and we can write to it, the result is 2.
  
  
! filter({expr1}, {expr2})                              *filter()*
!               {expr1} must be a |List| or a |Dictionary|.
!               For each item in {expr1} evaluate {expr2} and when the result
                is zero remove the item from the |List| or |Dictionary|.
!               {expr2} must be a |string| or |Funcref|.
!               
!               if {expr2} is a |string|, inside {expr2} |v:val| has the value
!               of the current item.  For a |Dictionary| |v:key| has the key
!               of the current item.
                Examples: >
!                       call filter(mylist, 'v:val !~ "OLD"')
  <             Removes the items where "OLD" appears. >
!                       call filter(mydict, 'v:key >= 8')
  <             Removes the items with a key below 8. >
!                       call filter(var, 0)
  <             Removes all the items, thus clears the |List| or |Dictionary|.
  
!               Note that {expr2} is the result of expression and is then
                used as an expression again.  Often it is good to use a
                |literal-string| to avoid having to double backslashes.
  
+               If {expr2} is a |Funcref| it must take two arguments:
+                       1. the key or the index of the current item.
+                       2. the value of the current item.
+               The function must return TRUE if the item should be kept.
+               Example that keeps the odd items of a list: >
+                       func Odd(idx, val)
+                         return a:idx % 2 == 1
+                       endfunc
+                       call filter(mylist, function('Odd'))
+ <
                The operation is done in-place.  If you want a |List| or
                |Dictionary| to remain unmodified make a copy first: >
                        :let l = filter(copy(mylist), 'v:val =~ "KEEP"')
  
! <             Returns {expr1}, the |List| or |Dictionary| that was filtered.
!               When an error is encountered while evaluating {expr2} no
!               further items in {expr1} are processed.  When {expr2} is a
!               Funcref errors inside a function are ignored, unless it was
!               defined with the "abort" flag.
  
  
  finddir({name}[, {path}[, {count}]])                          *finddir()*
***************
*** 4908,4936 ****
                See |lua-luaeval| for more details.
                {only available when compiled with the |+lua| feature}
  
! map({expr}, {string})                                 *map()*
!               {expr} must be a |List| or a |Dictionary|.
!               Replace each item in {expr} with the result of evaluating
!               {string}.
!               Inside {string} |v:val| has the value of the current item.
!               For a |Dictionary| |v:key| has the key of the current item
!               and for a |List| |v:key| has the index of the current item.
                Example: >
                        :call map(mylist, '"> " . v:val . " <"')
  <             This puts "> " before and " <" after each item in "mylist".
  
!               Note that {string} is the result of an expression and is then
                used as an expression again.  Often it is good to use a
                |literal-string| to avoid having to double backslashes.  You
                still have to double ' quotes
  
                The operation is done in-place.  If you want a |List| or
                |Dictionary| to remain unmodified make a copy first: >
                        :let tlist = map(copy(mylist), ' v:val . "\t"')
  
! <             Returns {expr}, the |List| or |Dictionary| that was filtered.
!               When an error is encountered while evaluating {string} no
!               further items in {expr} are processed.
  
  
  maparg({name}[, {mode} [, {abbr} [, {dict}]]])                        
*maparg()*
--- 5051,5093 ----
                See |lua-luaeval| for more details.
                {only available when compiled with the |+lua| feature}
  
! map({expr1}, {expr2})                                 *map()*
!               {expr1} must be a |List| or a |Dictionary|.
!               Replace each item in {expr1} with the result of evaluating
!               {expr2}.  {expr2} must be a |string| or |Funcref|.
!               
!               If {expr2} is a |string|, inside {expr2} |v:val| has the value
!               of the current item.  For a |Dictionary| |v:key| has the key
!               of the current item and for a |List| |v:key| has the index of
!               the current item.
                Example: >
                        :call map(mylist, '"> " . v:val . " <"')
  <             This puts "> " before and " <" after each item in "mylist".
  
!               Note that {expr2} is the result of an expression and is then
                used as an expression again.  Often it is good to use a
                |literal-string| to avoid having to double backslashes.  You
                still have to double ' quotes
  
+               If {expr2} is a |Funcref| it is called with two arguments:
+                       1. The key or the index of the current item.
+                       2. the value of the current item.
+               The function must return the new value of the item. Example
+               that changes each value by "key-value": >
+                       func KeyValue(key, val)
+                         return a:key . '-' . a:val
+                       endfunc
+                       call map(myDict, function('KeyValue'))
+ <
                The operation is done in-place.  If you want a |List| or
                |Dictionary| to remain unmodified make a copy first: >
                        :let tlist = map(copy(mylist), ' v:val . "\t"')
  
! <             Returns {expr1}, the |List| or |Dictionary| that was filtered.
!               When an error is encountered while evaluating {expr2} no
!               further items in {expr1} are processed.  When {expr2} is a
!               Funcref errors inside a function are ignored, unless it was
!               defined with the "abort" flag.
  
  
  maparg({name}[, {mode} [, {abbr} [, {dict}]]])                        
*maparg()*
*** ../vim-7.4.1988/src/Makefile        2016-07-02 20:27:29.953436359 +0200
--- src/Makefile        2016-07-04 21:41:54.026484719 +0200
***************
*** 2031,2036 ****
--- 2031,2037 ----
        test_farsi \
        test_feedkeys \
        test_file_perm \
+       test_filter_map \
        test_fnamemodify \
        test_glob2regpat \
        test_goto \
*** ../vim-7.4.1988/src/eval.c  2016-07-01 18:16:47.485936367 +0200
--- src/eval.c  2016-07-04 22:03:47.570793571 +0200
***************
*** 6375,6381 ****
        return TRUE;
      }
  
!     /* For VAR_FUNC and VAR_PARTIAL only compare the function name. */
      if ((tv1->v_type == VAR_FUNC
                || (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL))
            && (tv2->v_type == VAR_FUNC
--- 6375,6382 ----
        return TRUE;
      }
  
!     /* For VAR_FUNC and VAR_PARTIAL compare the function name, bound dict and
!      * arguments. */
      if ((tv1->v_type == VAR_FUNC
                || (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL))
            && (tv2->v_type == VAR_FUNC
***************
*** 11852,11858 ****
  }
  
  static void filter_map(typval_T *argvars, typval_T *rettv, int map);
! static int filter_map_one(typval_T *tv, char_u *expr, int map, int *remp);
  
  /*
   * Implementation of map() and filter().
--- 11853,11859 ----
  }
  
  static void filter_map(typval_T *argvars, typval_T *rettv, int map);
! static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp);
  
  /*
   * Implementation of map() and filter().
***************
*** 11860,11867 ****
      static void
  filter_map(typval_T *argvars, typval_T *rettv, int map)
  {
!     char_u    buf[NUMBUFLEN];
!     char_u    *expr;
      listitem_T        *li, *nli;
      list_T    *l = NULL;
      dictitem_T        *di;
--- 11861,11867 ----
      static void
  filter_map(typval_T *argvars, typval_T *rettv, int map)
  {
!     typval_T  *expr;
      listitem_T        *li, *nli;
      list_T    *l = NULL;
      dictitem_T        *di;
***************
*** 11896,11909 ****
        return;
      }
  
!     expr = get_tv_string_buf_chk(&argvars[1], buf);
      /* On type errors, the preceding call has already displayed an error
       * message.  Avoid a misleading error message for an empty string that
       * was not passed as argument. */
!     if (expr != NULL)
      {
        prepare_vimvar(VV_VAL, &save_val);
-       expr = skipwhite(expr);
  
        /* We reset "did_emsg" to be able to detect whether an error
         * occurred during evaluation of the expression. */
--- 11896,11908 ----
        return;
      }
  
!     expr = &argvars[1];
      /* On type errors, the preceding call has already displayed an error
       * message.  Avoid a misleading error message for an empty string that
       * was not passed as argument. */
!     if (expr->v_type != VAR_UNKNOWN)
      {
        prepare_vimvar(VV_VAL, &save_val);
  
        /* We reset "did_emsg" to be able to detect whether an error
         * occurred during evaluation of the expression. */
***************
*** 11975,11995 ****
  }
  
      static int
! filter_map_one(typval_T *tv, char_u *expr, int map, int *remp)
  {
      typval_T  rettv;
      char_u    *s;
      int               retval = FAIL;
  
      copy_tv(tv, &vimvars[VV_VAL].vv_tv);
!     s = expr;
!     if (eval1(&s, &rettv, TRUE) == FAIL)
!       goto theend;
!     if (*s != NUL)  /* check for trailing chars after expr */
!     {
!       EMSG2(_(e_invexpr2), s);
!       clear_tv(&rettv);
!       goto theend;
      }
      if (map)
      {
--- 11974,12017 ----
  }
  
      static int
! filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
  {
      typval_T  rettv;
+     typval_T  argv[3];
      char_u    *s;
      int               retval = FAIL;
+     int               dummy;
  
      copy_tv(tv, &vimvars[VV_VAL].vv_tv);
!     argv[0] = vimvars[VV_KEY].vv_tv;
!     argv[1] = vimvars[VV_VAL].vv_tv;
!     s = expr->vval.v_string;
!     if (expr->v_type == VAR_FUNC)
!     {
!       if (call_func(s, (int)STRLEN(s),
!                   &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL, NULL) == FAIL)
!           goto theend;
!     }
!     else if (expr->v_type == VAR_PARTIAL)
!     {
!       partial_T   *partial = expr->vval.v_partial;
! 
!       s = partial->pt_name;
!       if (call_func(s, (int)STRLEN(s),
!                   &rettv, 2, argv, 0L, 0L, &dummy, TRUE, partial, NULL)
!                                                                     == FAIL)
!           goto theend;
!     }
!     else
!     {
!       s = skipwhite(s);
!       if (eval1(&s, &rettv, TRUE) == FAIL)
!           goto theend;
!       if (*s != NUL)  /* check for trailing chars after expr */
!       {
!           EMSG2(_(e_invexpr2), s);
!           goto theend;
!       }
      }
      if (map)
      {
*** ../vim-7.4.1988/src/testdir/test_alot.vim   2016-07-01 14:04:36.422846118 
+0200
--- src/testdir/test_alot.vim   2016-07-04 21:41:54.038484538 +0200
***************
*** 12,17 ****
--- 12,18 ----
  source test_feedkeys.vim
  source test_fnamemodify.vim
  source test_file_perm.vim
+ source test_filter_map.vim
  source test_glob2regpat.vim
  source test_goto.vim
  source test_help_tagjump.vim
*** ../vim-7.4.1988/src/testdir/test_filter_map.vim     2016-07-04 
22:27:37.193518879 +0200
--- src/testdir/test_filter_map.vim     2016-07-04 21:41:54.038484538 +0200
***************
*** 0 ****
--- 1,77 ----
+ " Test filter() and map()
+ 
+ " list with expression string
+ func Test_filter_map_list_expr_string()
+   " filter()
+   call assert_equal([2, 3, 4], filter([1, 2, 3, 4], 'v:val > 1'))
+   call assert_equal([3, 4], filter([1, 2, 3, 4], 'v:key > 1'))
+ 
+   " map()
+   call assert_equal([2, 4, 6, 8], map([1, 2, 3, 4], 'v:val * 2'))
+   call assert_equal([0, 2, 4, 6], map([1, 2, 3, 4], 'v:key * 2'))
+ endfunc
+ 
+ " dict with expression string
+ func Test_filter_map_dict_expr_string()
+   let dict = {"foo": 1, "bar": 2, "baz": 3}
+ 
+   " filter()
+   call assert_equal({"bar": 2, "baz": 3}, filter(copy(dict), 'v:val > 1'))
+   call assert_equal({"foo": 1, "baz": 3}, filter(copy(dict), 'v:key > "bar"'))
+ 
+   " map()
+   call assert_equal({"foo": 2, "bar": 4, "baz": 6}, map(copy(dict), 'v:val * 
2'))
+   call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, map(copy(dict), 
'v:key[0]'))
+ endfunc
+ 
+ " list with funcref
+ func Test_filter_map_list_expr_funcref()
+   " filter()
+   func! s:filter1(index, val) abort
+     return a:val > 1
+   endfunc
+   call assert_equal([2, 3, 4], filter([1, 2, 3, 4], function('s:filter1')))
+ 
+   func! s:filter2(index, val) abort
+     return a:index > 1
+   endfunc
+   call assert_equal([3, 4], filter([1, 2, 3, 4], function('s:filter2')))
+ 
+   " map()
+   func! s:filter3(index, val) abort
+     return a:val * 2
+   endfunc
+   call assert_equal([2, 4, 6, 8], map([1, 2, 3, 4], function('s:filter3')))
+ 
+   func! s:filter4(index, val) abort
+     return a:index * 2
+   endfunc
+   call assert_equal([0, 2, 4, 6], map([1, 2, 3, 4], function('s:filter4')))
+ endfunc
+ 
+ " dict with funcref
+ func Test_filter_map_dict_expr_funcref()
+   let dict = {"foo": 1, "bar": 2, "baz": 3}
+ 
+   " filter()
+   func! s:filter1(key, val) abort
+     return a:val > 1
+   endfunc
+   call assert_equal({"bar": 2, "baz": 3}, filter(copy(dict), 
function('s:filter1')))
+ 
+   func! s:filter2(key, val) abort
+     return a:key > "bar"
+   endfunc
+   call assert_equal({"foo": 1, "baz": 3}, filter(copy(dict), 
function('s:filter2')))
+ 
+   " map()
+   func! s:filter3(key, val) abort
+     return a:val * 2
+   endfunc
+   call assert_equal({"foo": 2, "bar": 4, "baz": 6}, map(copy(dict), 
function('s:filter3')))
+ 
+   func! s:filter4(key, val) abort
+     return a:key[0]
+   endfunc
+   call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, map(copy(dict), 
function('s:filter4')))
+ endfunc
*** ../vim-7.4.1988/src/testdir/test_partial.vim        2016-06-02 
17:46:16.248499026 +0200
--- src/testdir/test_partial.vim        2016-07-04 21:41:54.038484538 +0200
***************
*** 14,19 ****
--- 14,27 ----
    return a:one < a:two ? 1 : -1
  endfunc
  
+ func MyMap(sub, index, val)
+   return a:val - a:sub
+ endfunc
+ 
+ func MyFilter(threshold, index, val)
+   return a:val > a:threshold
+ endfunc
+ 
  func Test_partial_args()
    let Cb = function('MyFunc', ["foo", "bar"])
  
***************
*** 36,41 ****
--- 44,59 ----
    call assert_equal([1, 2, 3], sort([3, 1, 2], Sort))
    let Sort = function('MySort', [0])
    call assert_equal([3, 2, 1], sort([3, 1, 2], Sort))
+ 
+   let Map = function('MyMap', [2])
+   call assert_equal([-1, 0, 1], map([1, 2, 3], Map))
+   let Map = function('MyMap', [3])
+   call assert_equal([-2, -1, 0], map([1, 2, 3], Map))
+ 
+   let Filter = function('MyFilter', [1])
+   call assert_equal([2, 3], filter([1, 2, 3], Filter))
+   let Filter = function('MyFilter', [2])
+   call assert_equal([3], filter([1, 2, 3], Filter))
  endfunc
  
  func MyDictFunc(arg1, arg2) dict
***************
*** 60,65 ****
--- 78,86 ----
    call assert_equal("hello/xxx/yyy", Cb("xxx", "yyy"))
    call assert_fails('Cb("fff")', 'E492:')
  
+   let Cb = function('MyDictFunc', dict)
+   call assert_equal({"foo": "hello/foo/1", "bar": "hello/bar/2"}, map({"foo": 
1, "bar": 2}, Cb))
+ 
    let dict = {"tr": function('tr', ['hello', 'h', 'H'])}
    call assert_equal("Hello", dict.tr())
  endfunc
*** ../vim-7.4.1988/src/version.c       2016-07-03 17:47:21.862812533 +0200
--- src/version.c       2016-07-04 21:52:06.049277372 +0200
***************
*** 760,761 ****
--- 760,763 ----
  {   /* Add new patch number below this line */
+ /**/
+     1989,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
201. When somebody asks you where you are, you tell them in which chat room.

 /// 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