patch 9.1.1668: items() does not work for Blobs Commit: https://github.com/vim/vim/commit/da34f84847d40dc5b1eaad477440e513968047dc Author: Yegappan Lakshmanan <yegap...@yahoo.com> Date: Sat Aug 23 06:18:34 2025 -0400
patch 9.1.1668: items() does not work for Blobs Problem: items() does not work for Blobs Solution: Extend items() to support Blob (Yegappan Lakshmanan). closes: #18080 Signed-off-by: Yegappan Lakshmanan <yegap...@yahoo.com> Signed-off-by: Christian Brabandt <c...@256bit.org> diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 7cac6b9a5..9ab97ecab 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1,4 +1,4 @@ -*builtin.txt* For Vim version 9.1. Last change: 2025 Aug 20 +*builtin.txt* For Vim version 9.1. Last change: 2025 Aug 23 VIM REFERENCE MANUAL by Bram Moolenaar @@ -344,7 +344,7 @@ isinf({expr}) Number determine if {expr} is infinity value (positive or negative) islocked({expr}) Number |TRUE| if {expr} is locked isnan({expr}) Number |TRUE| if {expr} is NaN -items({expr}) List key-value pairs in {expr} +items({expr}) List key/index-value pairs in {expr} job_getchannel({job}) Channel get the channel handle for {job} job_info([{job}]) Dict get information about {job} job_setoptions({job}, {options}) none set options for {job} @@ -6314,7 +6314,8 @@ items({expr}) *items()* Return a |List| with all key/index and value pairs of {expr}. Each |List| item is a list with two items: - for a |Dict|: the key and the value - - for a |List|, |Tuple| or |String|: the index and the value + - for a |List|, |Tuple|, |Blob| or |String|: the index and the + value The returned |List| is in arbitrary order for a |Dict|, otherwise it's in ascending order of the index. @@ -6328,6 +6329,7 @@ items({expr}) *items()* echo items([1, 2, 3]) echo items(('a', 'b', 'c')) echo items("foobar") + echo items(0z0102) < Can also be used as a |method|: > mydict->items() diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 0b5245e89..ce05623b5 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -1,4 +1,4 @@ -*usr_41.txt* For Vim version 9.1. Last change: 2025 Aug 18 +*usr_41.txt* For Vim version 9.1. Last change: 2025 Aug 23 VIM USER MANUAL - by Bram Moolenaar @@ -920,6 +920,7 @@ Blob manipulation: *blob-functions* reverse() reverse the order of numbers in a blob index() index of a value in a Blob indexof() index in a Blob where an expression is true + items() get List of Blob index-value pairs Other computation: *bitwise-function* and() bitwise AND diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 745cf7242..6ea525759 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -41648,6 +41648,8 @@ Other new features ~ - Add the new default highlighting groups "Bold", "Italic" and "BoldItalic" for use in syntax scripts. +- |items()| function now supports Blob. + *changed-9.2* Changed~ ------- diff --git a/src/blob.c b/src/blob.c index 9cdd504b6..d8d54313f 100644 --- a/src/blob.c +++ b/src/blob.c @@ -289,6 +289,38 @@ blob2string(blob_T *blob, char_u **tofree, char_u *numbuf) return *tofree; } +/* + * "items(blob)" function + * Converts a Blob into a List of [index, byte] pairs. + * Caller must have already checked that argvars[0] is a Blob. + * A null blob behaves like an empty blob. + */ + void +blob2items(typval_T *argvars, typval_T *rettv) +{ + blob_T *blob = argvars[0].vval.v_blob; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + for (int i = 0; i < blob_len(blob); i++) + { + list_T *l2 = list_alloc(); + if (l2 == NULL) + return; + + if (list_append_list(rettv->vval.v_list, l2) == FAIL) + { + vim_free(l2); + return; + } + + if (list_append_number(l2, i) == FAIL + || list_append_number(l2, blob_get(blob, i)) == FAIL) + return; + } +} + /* * Convert a string variable, in the format of blob2string(), to a blob. * Return NULL when conversion failed. diff --git a/src/dict.c b/src/dict.c index b4e971e56..d2010082e 100644 --- a/src/dict.c +++ b/src/dict.c @@ -1557,9 +1557,7 @@ dict2list(typval_T *argvars, typval_T *rettv, dict2list_T what) if (rettv_list_alloc(rettv) == FAIL) return; - if ((what == DICT2LIST_ITEMS - ? check_for_string_list_tuple_or_dict_arg(argvars, 0) - : check_for_dict_arg(argvars, 0)) == FAIL) + if (check_for_dict_arg(argvars, 0) == FAIL) return; d = argvars[0].vval.v_dict; @@ -1612,19 +1610,12 @@ dict2list(typval_T *argvars, typval_T *rettv, dict2list_T what) } /* - * "items(dict)" function + * "items()" function */ void -f_items(typval_T *argvars, typval_T *rettv) +dict2items(typval_T *argvars, typval_T *rettv) { - if (argvars[0].v_type == VAR_STRING) - string2items(argvars, rettv); - else if (argvars[0].v_type == VAR_LIST) - list2items(argvars, rettv); - else if (argvars[0].v_type == VAR_TUPLE) - tuple2items(argvars, rettv); - else - dict2list(argvars, rettv, DICT2LIST_ITEMS); + dict2list(argvars, rettv, DICT2LIST_ITEMS); } /* diff --git a/src/evalfunc.c b/src/evalfunc.c index 419780f38..2dced8e3f 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -97,6 +97,7 @@ static void f_inputsecret(typval_T *argvars, typval_T *rettv); static void f_interrupt(typval_T *argvars, typval_T *rettv); static void f_invert(typval_T *argvars, typval_T *rettv); static void f_islocked(typval_T *argvars, typval_T *rettv); +static void f_items(typval_T *argvars, typval_T *rettv); static void f_keytrans(typval_T *argvars, typval_T *rettv); static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv); static void f_libcall(typval_T *argvars, typval_T *rettv); @@ -726,11 +727,12 @@ arg_list_tuple_dict_blob_or_string( || type->tt_type == VAR_STRING || type_any_or_unknown(type)) return OK; - arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); + + semsg(_(e_list_tuple_dict_blob_or_string_required_for_argument_nr), + context->arg_idx + 1); return FAIL; } - /* * Check second argument of map(), filter(), foreach(). */ @@ -1248,7 +1250,7 @@ static argcheck_T arg1_list_number[] = {arg_list_number}; static argcheck_T arg1_reverse[] = {arg_reverse}; static argcheck_T arg1_list_or_tuple_or_dict[] = {arg_list_or_tuple_or_dict}; static argcheck_T arg1_list_string[] = {arg_list_string}; -static argcheck_T arg1_string_list_tuple_or_dict[] = {arg_string_list_tuple_or_dict}; +static argcheck_T arg1_list_tuple_dict_blob_or_string[] = {arg_list_tuple_dict_blob_or_string}; static argcheck_T arg1_lnum[] = {arg_lnum}; static argcheck_T arg1_number[] = {arg_number}; static argcheck_T arg1_string[] = {arg_string}; @@ -2432,7 +2434,7 @@ static funcentry_T global_functions[] = 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_string_list_tuple_or_dict, + {"items", 1, 1, FEARG_1, arg1_list_tuple_dict_blob_or_string, ret_list_items, f_items}, {"job_getchannel", 1, 1, FEARG_1, arg1_job, ret_channel, JOB_FUNC(f_job_getchannel)}, @@ -8716,6 +8718,26 @@ f_islocked(typval_T *argvars, typval_T *rettv) clear_lval(&lv); } +/* + * "items(dict)" function + */ + static void +f_items(typval_T *argvars, typval_T *rettv) +{ + if (argvars[0].v_type == VAR_STRING) + string2items(argvars, rettv); + else if (argvars[0].v_type == VAR_LIST) + list2items(argvars, rettv); + else if (argvars[0].v_type == VAR_TUPLE) + tuple2items(argvars, rettv); + else if (argvars[0].v_type == VAR_BLOB) + blob2items(argvars, rettv); + else if (argvars[0].v_type == VAR_DICT) + dict2items(argvars, rettv); + else + semsg(_(e_list_tuple_dict_blob_or_string_required_for_argument_nr), 1); +} + /* * "keytrans()" function */ diff --git a/src/proto/blob.pro b/src/proto/blob.pro index 06959aa7b..f24c268ae 100644 --- a/src/proto/blob.pro +++ b/src/proto/blob.pro @@ -13,6 +13,7 @@ int blob_equal(blob_T *b1, blob_T *b2); int read_blob(FILE *fd, typval_T *rettv, off_T offset, off_T size_arg); int write_blob(FILE *fd, blob_T *blob); char_u *blob2string(blob_T *blob, char_u **tofree, char_u *numbuf); +void blob2items(typval_T *argvars, typval_T *rettv); blob_T *string2blob(char_u *str); int blob_slice_or_index(blob_T *blob, int is_range, varnumber_T n1, varnumber_T n2, int exclusive, typval_T *rettv); int check_blob_index(long bloblen, varnumber_T n1, int quiet); diff --git a/src/proto/dict.pro b/src/proto/dict.pro index 421b16946..2507f6af1 100644 --- a/src/proto/dict.pro +++ b/src/proto/dict.pro @@ -46,7 +46,7 @@ long dict_count(dict_T *d, typval_T *needle, int ic); void dict_extend_func(typval_T *argvars, type_T *type, char *func_name, char_u *arg_errmsg, int is_new, typval_T *rettv); void dict_filter_map(dict_T *d, filtermap_T filtermap, type_T *argtype, char *func_name, char_u *arg_errmsg, typval_T *expr, typval_T *rettv); void dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg); -void f_items(typval_T *argvars, typval_T *rettv); +void dict2items(typval_T *argvars, typval_T *rettv); void f_keys(typval_T *argvars, typval_T *rettv); void f_values(typval_T *argvars, typval_T *rettv); void dict_set_items_ro(dict_T *di); diff --git a/src/testdir/test_blob.vim b/src/testdir/test_blob.vim index 93005a617..f4177a63d 100644 --- a/src/testdir/test_blob.vim +++ b/src/testdir/test_blob.vim @@ -865,4 +865,15 @@ func Test_indexof() call assert_fails('let i = indexof(b, " ")', 'E15:') endfunc +" Test for using the items() function with a blob +func Test_blob_items() + let lines =<< trim END + call assert_equal([[0, 0xAA], [1, 0xBB], [2, 0xCC]], 0zAABBCC->items()) + call assert_equal([[0, 0]], 0z00->items()) + call assert_equal([], 0z->items()) + call assert_equal([], test_null_blob()->items()) + END + call v9.CheckSourceLegacyAndVim9Success(lines) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_listdict.vim b/src/testdir/test_listdict.vim index f806eb897..6d18b9179 100644 --- a/src/testdir/test_listdict.vim +++ b/src/testdir/test_listdict.vim @@ -230,7 +230,7 @@ func Test_list_items() endfor call assert_equal([[0, 'a'], [1, 'b'], [2, 'c']], r) - call assert_fails('call items(3)', 'E1225:') + call assert_fails('call items(3)', 'E1251:') endfunc func Test_string_items() diff --git a/src/testdir/test_method.vim b/src/testdir/test_method.vim index 797aa60ce..7413ed28d 100644 --- a/src/testdir/test_method.vim +++ b/src/testdir/test_method.vim @@ -76,7 +76,7 @@ func Test_string_method() eval "a b c"->strtrans()->assert_equal('a^Mb^[c') eval "aあb"->strwidth()->assert_equal(4) eval 'abc'->substitute('b', 'x', '')->assert_equal('axc') - call assert_fails('eval 123->items()', 'E1225:') + call assert_fails('eval 123->items()', 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1') eval 'abc'->printf('the %s arg')->assert_equal('the abc arg') endfunc diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index eec62536f..8e7bd6139 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -1655,7 +1655,7 @@ enddef def Test_foreach() CheckFeature job - v9.CheckSourceDefAndScriptFailure(['foreach(test_null_job(), "")'], ['E1013: Argument 1: type mismatch, expected list<any> but got job', 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1']) + v9.CheckSourceDefAndScriptFailure(['foreach(test_null_job(), "")'], 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1') enddef def Test_fullcommand() @@ -2492,15 +2492,19 @@ def Test_islocked() enddef def Test_items() - v9.CheckSourceDefFailure(['123->items()'], 'E1225:') + v9.CheckSourceDefFailure(['123->items()'], 'E1251: List, Tuple, Dictionary, Blob or String required for argument 1') + + # Dict 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')) + # List assert_equal([[0, 'a'], [1, 'b']], ['a', 'b']->items()) assert_equal([], []->items()) assert_equal([], test_null_list()->items()) + # String assert_equal([[0, 'a'], [1, '웃'], [2, 'ć']], 'a웃ć'->items()) assert_equal([], ''->items()) assert_equal([], test_null_string()->items()) diff --git a/src/version.c b/src/version.c index 197d25dee..aa3142c5b 100644 --- a/src/version.c +++ b/src/version.c @@ -724,6 +724,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1668, /**/ 1667, /**/ -- -- 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 vim_dev+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1upmRh-00ElWR-MW%40256bit.org.