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>
diff --git a/src/list.c b/src/list.c
index b50cb03..e9f1ae3 100644
--- a/src/list.c
+++ b/src/list.c
@@ -2325,7 +2325,7 @@
 }
 
 /*
- * 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 @@
     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 @@
 	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 @@
 }
 
 /*
- * 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 @@
 	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 @@
 		clear_tv(&newtv);
 		break;
 	    }
-	    if (filtermap != FILTERMAP_FILTER)
+	    if (filtermap != FILTERMAP_FOREACH)
 	    {
-		if (filtermap == FILTERMAP_MAP && argtype != NULL
-			&& check_typval_arg_type(
-			    argtype->tt_member, &newtv,
-			    func_name, 0) == FAIL)
+		if (filtermap != FILTERMAP_FILTER)
 		{
-		    clear_tv(&newtv);
-		    break;
+		    if (filtermap == FILTERMAP_MAP && argtype != NULL
+			&& check_typval_arg_type(
+						 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 @@
 		    break;
 	    }
 	    else if (filtermap == FILTERMAP_FILTER && rem)
-		listitem_remove(l, li);
+		    listitem_remove(l, li);
 	    ++idx;
 	}
     }
@@ -2519,7 +2536,7 @@
 }
 
 /*
- * 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 @@
     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 @@
 }
 
 /*
+ * "foreach()" function
+ */
+    void
+f_foreach(typval_T *argvars, typval_T *rettv)
+{
+    filter_map(argvars, rettv, FILTERMAP_FOREACH);
+}
+
+/*
  * "add(list, item)" function
  */
     static void