diff src/list.c @ 34126:da670b1549b3 v9.1.0027

patch 9.1.0027: Vim is missing a foreach() func Commit: https://github.com/vim/vim/commit/e79e2077607e8f829ba823308c91104a795736ba Author: Ernie Rael <errael@raelity.com> Date: Sat Jan 13 11:47:33 2024 +0100 patch 9.1.0027: Vim is missing a foreach() func Problem: Vim is missing a foreach() func Solution: Implement foreach({expr1}, {expr2}) function, which applies {expr2} for each item in {expr1} without changing it (Ernie Rael) closes: #12166 Signed-off-by: Ernie Rael <errael@raelity.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Sat, 13 Jan 2024 12:00:06 +0100
parents aceaf677dd92
children
line wrap: on
line diff
--- a/src/list.c
+++ b/src/list.c
@@ -2325,7 +2325,7 @@ f_uniq(typval_T *argvars, typval_T *rett
 }
 
 /*
- * Handle one item for map() and filter().
+ * Handle one item for map(), filter(), foreach().
  * Sets v:val to "tv".  Caller must set v:key.
  */
     int
@@ -2341,6 +2341,17 @@ filter_map_one(
     int		retval = FAIL;
 
     copy_tv(tv, get_vim_var_tv(VV_VAL));
+
+    newtv->v_type = VAR_UNKNOWN;
+    if (filtermap == FILTERMAP_FOREACH && expr->v_type == VAR_STRING)
+    {
+	// foreach() is not limited to an expression
+	do_cmdline_cmd(expr->vval.v_string);
+	if (!did_emsg)
+	    retval = OK;
+	goto theend;
+    }
+
     argv[0] = *get_vim_var_tv(VV_KEY);
     argv[1] = *get_vim_var_tv(VV_VAL);
     if (eval_expr_typval(expr, FALSE, argv, 2, fc, newtv) == FAIL)
@@ -2360,6 +2371,8 @@ filter_map_one(
 	if (error)
 	    goto theend;
     }
+    else if (filtermap == FILTERMAP_FOREACH)
+	clear_tv(newtv);
     retval = OK;
 theend:
     clear_tv(get_vim_var_tv(VV_VAL));
@@ -2367,8 +2380,8 @@ theend:
 }
 
 /*
- * Implementation of map() and filter() for a List.  Apply "expr" to every item
- * in List "l" and return the result in "rettv".
+ * Implementation of map(), filter(), foreach() for a List.  Apply "expr" to
+ * every item in List "l" and return the result in "rettv".
  */
     static void
 list_filter_map(
@@ -2421,7 +2434,8 @@ list_filter_map(
 	int		stride = l->lv_u.nonmat.lv_stride;
 
 	// List from range(): loop over the numbers
-	if (filtermap != FILTERMAP_MAPNEW)
+	// NOTE: foreach() returns the range_list_item
+	if (filtermap != FILTERMAP_MAPNEW && filtermap != FILTERMAP_FOREACH)
 	{
 	    l->lv_first = NULL;
 	    l->lv_u.mat.lv_last = NULL;
@@ -2444,27 +2458,30 @@ list_filter_map(
 		clear_tv(&newtv);
 		break;
 	    }
-	    if (filtermap != FILTERMAP_FILTER)
+	    if (filtermap != FILTERMAP_FOREACH)
 	    {
-		if (filtermap == FILTERMAP_MAP && argtype != NULL
+		if (filtermap != FILTERMAP_FILTER)
+		{
+		    if (filtermap == FILTERMAP_MAP && argtype != NULL
 			&& check_typval_arg_type(
-			    argtype->tt_member, &newtv,
-			    func_name, 0) == FAIL)
-		{
-		    clear_tv(&newtv);
-		    break;
+						 argtype->tt_member, &newtv,
+						 func_name, 0) == FAIL)
+		    {
+			clear_tv(&newtv);
+			break;
+		    }
+		    // map(), mapnew(): always append the new value to the
+		    // list
+		    if (list_append_tv_move(filtermap == FILTERMAP_MAP
+					    ? l : l_ret, &newtv) == FAIL)
+			break;
 		}
-		// map(), mapnew(): always append the new value to the
-		// list
-		if (list_append_tv_move(filtermap == FILTERMAP_MAP
-			    ? l : l_ret, &newtv) == FAIL)
-		    break;
-	    }
-	    else if (!rem)
-	    {
-		// filter(): append the list item value when not rem
-		if (list_append_tv_move(l, &tv) == FAIL)
-		    break;
+		else if (!rem)
+		{
+		    // filter(): append the list item value when not rem
+		    if (list_append_tv_move(l, &tv) == FAIL)
+			break;
+		}
 	    }
 
 	    val += stride;
@@ -2508,7 +2525,7 @@ list_filter_map(
 		    break;
 	    }
 	    else if (filtermap == FILTERMAP_FILTER && rem)
-		listitem_remove(l, li);
+		    listitem_remove(l, li);
 	    ++idx;
 	}
     }
@@ -2519,7 +2536,7 @@ list_filter_map(
 }
 
 /*
- * Implementation of map() and filter().
+ * Implementation of map(), filter() and foreach().
  */
     static void
 filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
@@ -2527,16 +2544,19 @@ filter_map(typval_T *argvars, typval_T *
     typval_T	*expr;
     char	*func_name = filtermap == FILTERMAP_MAP ? "map()"
 				  : filtermap == FILTERMAP_MAPNEW ? "mapnew()"
-				  : "filter()";
+				  : filtermap == FILTERMAP_FILTER ? "filter()"
+				  : "foreach()";
     char_u	*arg_errmsg = (char_u *)(filtermap == FILTERMAP_MAP
 							 ? N_("map() argument")
 				       : filtermap == FILTERMAP_MAPNEW
 						      ? N_("mapnew() argument")
-						    : N_("filter() argument"));
+				       : filtermap == FILTERMAP_FILTER
+						      ? N_("filter() argument")
+						   : N_("foreach() argument"));
     int		save_did_emsg;
     type_T	*type = NULL;
 
-    // map() and filter() return the first argument, also on failure.
+    // map(), filter(), foreach() return the first argument, also on failure.
     if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING)
 	copy_tv(&argvars[0], rettv);
 
@@ -2630,6 +2650,15 @@ f_mapnew(typval_T *argvars, typval_T *re
 }
 
 /*
+ * "foreach()" function
+ */
+    void
+f_foreach(typval_T *argvars, typval_T *rettv)
+{
+    filter_map(argvars, rettv, FILTERMAP_FOREACH);
+}
+
+/*
  * "add(list, item)" function
  */
     static void