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.

Raspunde prin e-mail lui