To: vim_dev@googlegroups.com Subject: Patch 9.0.0327 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0327 Problem: items() does not work on a list. (Sergey Vlasov) Solution: Make items() work on a list. (closes #11013) Files: src/dict.c, src/list.c, src/proto/list.pro, src/evalfunc.c, src/testdir/test_listdict.vim, src/testdir/test_vim9_builtin.vim *** ../vim-9.0.0326/src/dict.c 2022-07-23 09:52:00.333814262 +0100 --- src/dict.c 2022-08-30 13:41:43.216959585 +0100 *************** *** 1440,1453 **** dictitem_remove(d, di); } /* ! * Turn a dict into a list: ! * "what" == 0: list of keys ! * "what" == 1: list of values ! * "what" == 2: list of items */ static void ! dict_list(typval_T *argvars, typval_T *rettv, int what) { list_T *l2; dictitem_T *di; --- 1440,1456 ---- dictitem_remove(d, di); } + typedef enum { + DICT2LIST_KEYS, + DICT2LIST_VALUES, + DICT2LIST_ITEMS, + } dict2list_T; + /* ! * Turn a dict into a list. */ static void ! dict2list(typval_T *argvars, typval_T *rettv, dict2list_T what) { list_T *l2; dictitem_T *di; *************** *** 1460,1475 **** if (rettv_list_alloc(rettv) == FAIL) return; ! if (in_vim9script() && check_for_dict_arg(argvars, 0) == FAIL) return; ! if (argvars[0].v_type != VAR_DICT) ! { ! emsg(_(e_dictionary_required)); ! return; ! } ! ! if ((d = argvars[0].vval.v_dict) == NULL) // empty dict behaves like an empty dict return; --- 1463,1475 ---- if (rettv_list_alloc(rettv) == FAIL) return; ! if ((what == DICT2LIST_ITEMS ! ? check_for_list_or_dict_arg(argvars, 0) ! : check_for_dict_arg(argvars, 0)) == FAIL) return; ! d = argvars[0].vval.v_dict; ! if (d == NULL) // empty dict behaves like an empty dict return; *************** *** 1486,1499 **** break; list_append(rettv->vval.v_list, li); ! if (what == 0) { // keys() li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; li->li_tv.vval.v_string = vim_strsave(di->di_key); } ! else if (what == 1) { // values() copy_tv(&di->di_tv, &li->li_tv); --- 1486,1499 ---- break; list_append(rettv->vval.v_list, li); ! if (what == DICT2LIST_KEYS) { // keys() li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; li->li_tv.vval.v_string = vim_strsave(di->di_key); } ! else if (what == DICT2LIST_VALUES) { // values() copy_tv(&di->di_tv, &li->li_tv); *************** *** 1533,1539 **** void f_items(typval_T *argvars, typval_T *rettv) { ! dict_list(argvars, rettv, 2); } /* --- 1533,1542 ---- void f_items(typval_T *argvars, typval_T *rettv) { ! if (argvars[0].v_type == VAR_LIST) ! list2items(argvars, rettv); ! else ! dict2list(argvars, rettv, DICT2LIST_ITEMS); } /* *************** *** 1542,1548 **** void f_keys(typval_T *argvars, typval_T *rettv) { ! dict_list(argvars, rettv, 0); } /* --- 1545,1551 ---- void f_keys(typval_T *argvars, typval_T *rettv) { ! dict2list(argvars, rettv, DICT2LIST_KEYS); } /* *************** *** 1551,1557 **** void f_values(typval_T *argvars, typval_T *rettv) { ! dict_list(argvars, rettv, 1); } /* --- 1554,1560 ---- void f_values(typval_T *argvars, typval_T *rettv) { ! dict2list(argvars, rettv, DICT2LIST_VALUES); } /* *** ../vim-9.0.0326/src/list.c 2022-06-16 11:31:58.000000000 +0100 --- src/list.c 2022-08-30 14:18:08.597689335 +0100 *************** *** 1053,1058 **** --- 1053,1090 ---- } /* + * "items(list)" function + * Caller must have already checked that argvars[0] is a List. + */ + void + list2items(typval_T *argvars, typval_T *rettv) + { + list_T *l = argvars[0].vval.v_list; + listitem_T *li; + varnumber_T idx; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + if (l == NULL) + return; // empty list behaves like an empty list + + // TODO: would be more efficient to not materialize the argument + CHECK_LIST_MATERIALIZE(l); + for (idx = 0, li = l->lv_first; li != NULL; li = li->li_next, ++idx) + { + list_T *l2 = list_alloc(); + + if (l2 == NULL) + break; + if (list_append_list(rettv->vval.v_list, l2) == FAIL + || list_append_number(l2, idx) == FAIL + || list_append_tv(l2, &li->li_tv) == FAIL) + break; + } + } + + /* * Extend "l1" with "l2". "l1" must not be NULL. * If "bef" is NULL append at the end, otherwise insert before this item. * Returns FAIL when out of memory. *** ../vim-9.0.0326/src/proto/list.pro 2022-06-27 23:15:12.000000000 +0100 --- src/proto/list.pro 2022-08-30 13:30:15.221832106 +0100 *************** *** 35,40 **** --- 35,41 ---- int list_assign_range(list_T *dest, list_T *src, long idx1_arg, long idx2, int empty_idx2, char_u *op, char_u *varname); void f_flatten(typval_T *argvars, typval_T *rettv); void f_flattennew(typval_T *argvars, typval_T *rettv); + void list2items(typval_T *argvars, typval_T *rettv); int list_extend(list_T *l1, list_T *l2, listitem_T *bef); int list_concat(list_T *l1, list_T *l2, typval_T *tv); list_T *list_slice(list_T *ol, long n1, long n2); *** ../vim-9.0.0326/src/evalfunc.c 2022-08-27 12:22:19.975008573 +0100 --- src/evalfunc.c 2022-08-30 14:25:00.305553403 +0100 *************** *** 2029,2035 **** ret_number_bool, f_islocked}, {"isnan", 1, 1, FEARG_1, arg1_float_or_nr, ret_number_bool, MATH_FUNC(f_isnan)}, ! {"items", 1, 1, FEARG_1, arg1_dict_any, ret_list_items, f_items}, {"job_getchannel", 1, 1, FEARG_1, arg1_job, ret_channel, JOB_FUNC(f_job_getchannel)}, --- 2029,2035 ---- ret_number_bool, f_islocked}, {"isnan", 1, 1, FEARG_1, arg1_float_or_nr, ret_number_bool, MATH_FUNC(f_isnan)}, ! {"items", 1, 1, FEARG_1, arg1_list_or_dict, ret_list_items, f_items}, {"job_getchannel", 1, 1, FEARG_1, arg1_job, ret_channel, JOB_FUNC(f_job_getchannel)}, *** ../vim-9.0.0326/src/testdir/test_listdict.vim 2022-08-14 12:07:06.918862666 +0100 --- src/testdir/test_listdict.vim 2022-08-30 14:26:28.797502394 +0100 *************** *** 198,203 **** --- 198,214 ---- call v9.CheckDefAndScriptFailure(lines, 'E1012:', 2) endfunc + func Test_list_items() + let r = [] + let l = ['a', 'b', 'c'] + for [idx, val] in items(l) + call extend(r, [[idx, val]]) + endfor + call assert_equal([[0, 'a'], [1, 'b'], [2, 'c']], r) + + call assert_fails('call items(3)', 'E1227:') + endfunc + " Test removing items in list func Test_list_func_remove() let lines =<< trim END *** ../vim-9.0.0326/src/testdir/test_vim9_builtin.vim 2022-08-28 18:52:06.671888918 +0100 --- src/testdir/test_vim9_builtin.vim 2022-08-30 14:30:13.397351833 +0100 *************** *** 2244,2253 **** enddef def Test_items() ! v9.CheckDefFailure(['[]->items()'], 'E1013: Argument 1: type mismatch, expected dict but got list') assert_equal([['a', 10], ['b', 20]], {'a': 10, 'b': 20}->items()) assert_equal([], {}->items()) assert_equal(['x', 'x'], {'a': 10, 'b': 20}->items()->map((_, _) => 'x')) enddef def Test_job_getchannel() --- 2244,2257 ---- enddef def Test_items() ! v9.CheckDefFailure(['"x"->items()'], 'E1013: Argument 1: type mismatch, expected list but got string') assert_equal([['a', 10], ['b', 20]], {'a': 10, 'b': 20}->items()) assert_equal([], {}->items()) assert_equal(['x', 'x'], {'a': 10, 'b': 20}->items()->map((_, _) => 'x')) + + assert_equal([[0, 'a'], [1, 'b']], ['a', 'b']->items()) + assert_equal([], []->items()) + assert_equal([], test_null_list()->items()) enddef def Test_job_getchannel() *** ../vim-9.0.0326/src/version.c 2022-08-30 11:53:42.748243478 +0100 --- src/version.c 2022-08-30 14:30:27.245341740 +0100 *************** *** 709,710 **** --- 709,712 ---- { /* Add new patch number below this line */ + /**/ + 327, /**/ -- There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. The other way is to make it so complicated that there are no obvious deficiencies. -C.A.R. Hoare /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///