diff src/list.c @ 26638:6fd15d82e898 v8.2.3848

patch 8.2.3848: cannot use reduce() for a string Commit: https://github.com/vim/vim/commit/0ccb5842f5fb103763d106c7aa364d758343c35a Author: rbtnn <naru123456789@gmail.com> Date: Sat Dec 18 18:33:46 2021 +0000 patch 8.2.3848: cannot use reduce() for a string Problem: Cannot use reduce() for a string. Solution: Make reduce() work with a string. (Naruhiko Nishino, closes https://github.com/vim/vim/issues/9366)
author Bram Moolenaar <Bram@vim.org>
date Sat, 18 Dec 2021 19:45:03 +0100
parents 0d2a709e2ff0
children 7c055fdd6200
line wrap: on
line diff
--- a/src/list.c
+++ b/src/list.c
@@ -314,6 +314,28 @@ listitem_alloc(void)
 }
 
 /*
+ * Make a typval_T of the first character of "input" and store it in "output".
+ * Return OK or FAIL.
+ */
+    static int
+tv_get_first_char(char_u *input, typval_T *output)
+{
+    char_u	buf[MB_MAXBYTES + 1];
+    int		len;
+
+    if (input == NULL || output == NULL)
+	return FAIL;
+
+    len = has_mbyte ? mb_ptr2len(input) : 1;
+    STRNCPY(buf, input, len);
+    buf[len] = NUL;
+    output->v_type = VAR_STRING;
+    output->vval.v_string = vim_strsave(buf);
+
+    return output->vval.v_string == NULL ? FAIL : OK;
+}
+
+/*
  * Free a list item, unless it was allocated together with the list itself.
  * Does not clear the value.  Does not notify watchers.
  */
@@ -2492,7 +2514,6 @@ filter_map(typval_T *argvars, typval_T *
 	    char_u	*p;
 	    typval_T	tv;
 	    garray_T	ga;
-	    char_u	buf[MB_MAXBYTES + 1];
 	    int		len;
 
 	    // set_vim_var_nr() doesn't set the type
@@ -2503,16 +2524,9 @@ filter_map(typval_T *argvars, typval_T *
 	    {
 	        typval_T newtv;
 
-		if (has_mbyte)
-		    len = mb_ptr2len(p);
-		else
-		    len = 1;
-
-		STRNCPY(buf, p, len);
-		buf[len] = NUL;
-
-		tv.v_type = VAR_STRING;
-		tv.vval.v_string = vim_strsave(buf);
+		if (tv_get_first_char(p, &tv) == FAIL)
+		    break;
+		len = STRLEN(tv.vval.v_string);
 
 		set_vim_var_nr(VV_KEY, idx);
 		if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
@@ -3248,12 +3262,17 @@ f_reduce(typval_T *argvars, typval_T *re
     partial_T   *partial = NULL;
     funcexe_T	funcexe;
     typval_T	argv[3];
-
-    if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB)
-    {
-	emsg(_(e_listblobreq));
+    int		r;
+    int		called_emsg_start = called_emsg;
+
+    if (in_vim9script()
+		   && check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL)
 	return;
-    }
+
+    if (argvars[0].v_type != VAR_STRING
+	    && argvars[0].v_type != VAR_LIST
+	    && argvars[0].v_type != VAR_BLOB)
+	semsg(_(e_string_list_or_blob_required), "reduce()");
 
     if (argvars[1].v_type == VAR_FUNC)
 	func_name = argvars[1].vval.v_string;
@@ -3278,8 +3297,6 @@ f_reduce(typval_T *argvars, typval_T *re
     {
 	list_T	    *l = argvars[0].vval.v_list;
 	listitem_T  *li = NULL;
-	int	    r;
-	int	    called_emsg_start = called_emsg;
 
 	if (l != NULL)
 	    CHECK_LIST_MATERIALIZE(l);
@@ -3319,6 +3336,43 @@ f_reduce(typval_T *argvars, typval_T *re
 	    l->lv_lock = prev_locked;
 	}
     }
+    else if (argvars[0].v_type == VAR_STRING)
+    {
+	char_u	*p = tv_get_string(&argvars[0]);
+	int     len;
+
+	if (argvars[2].v_type == VAR_UNKNOWN)
+	{
+	    if (*p == NUL)
+	    {
+		semsg(_(e_reduceempty), "String");
+		return;
+	    }
+	    if (tv_get_first_char(p, rettv) == FAIL)
+		return;
+	    p += STRLEN(rettv->vval.v_string);
+	}
+	else if (argvars[2].v_type != VAR_STRING)
+	{
+	    semsg(_(e_string_expected_for_argument_nr), 3);
+	    return;
+	}
+	else
+	    copy_tv(&argvars[2], rettv);
+
+	for ( ; *p != NUL; p += len)
+	{
+	    argv[0] = *rettv;
+	    if (tv_get_first_char(p, &argv[1]) == FAIL)
+		break;
+	    len = STRLEN(argv[1].vval.v_string);
+	    r = call_func(func_name, -1, rettv, 2, argv, &funcexe);
+	    clear_tv(&argv[0]);
+	    clear_tv(&argv[1]);
+	    if (r == FAIL || called_emsg != called_emsg_start)
+		break;
+	}
+    }
     else
     {
 	blob_T	*b = argvars[0].vval.v_blob;