mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	vim-patch:partial:9.0.0327: items() does not work on a list
Problem:    items() does not work on a list. (Sergey Vlasov)
Solution:   Make items() work on a list. (closes vim/vim#11013)
976f859763
Skip CHECK_LIST_MATERIALIZE.
Co-authored-by: Bram Moolenaar <Bram@vim.org>
			
			
This commit is contained in:
		| @@ -237,13 +237,6 @@ typedef enum { | ||||
|   EXPR_ISNOT,         ///< isnot | ||||
| } exprtype_T; | ||||
|  | ||||
| /// Type for dict_list function | ||||
| typedef enum { | ||||
|   kDictListKeys,  ///< List dictionary keys. | ||||
|   kDictListValues,  ///< List dictionary values. | ||||
|   kDictListItems,  ///< List dictionary contents: [keys, values]. | ||||
| } DictListType; | ||||
|  | ||||
| // Used for checking if local variables or arguments used in a lambda. | ||||
| extern bool *eval_lavars_used; | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,7 @@ | ||||
| #include "nvim/eval/executor.h" | ||||
| #include "nvim/eval/gc.h" | ||||
| #include "nvim/eval/typval.h" | ||||
| #include "nvim/eval/typval_defs.h" | ||||
| #include "nvim/eval/typval_encode.h" | ||||
| #include "nvim/eval/userfunc.h" | ||||
| #include "nvim/eval/vars.h" | ||||
| @@ -59,6 +60,13 @@ typedef struct { | ||||
|  | ||||
| typedef int (*ListSorter)(const void *, const void *); | ||||
|  | ||||
| /// Type for tv_dict2list() function | ||||
| typedef enum { | ||||
|   kDict2ListKeys,    ///< List dictionary keys. | ||||
|   kDict2ListValues,  ///< List dictionary values. | ||||
|   kDict2ListItems,   ///< List dictionary contents: [keys, values]. | ||||
| } DictListType; | ||||
|  | ||||
| #ifdef INCLUDE_GENERATED_DECLARATIONS | ||||
| # include "eval/typval.c.generated.h" | ||||
| #endif | ||||
| @@ -87,6 +95,8 @@ static const char e_string_or_list_required_for_argument_nr[] | ||||
|   = N_("E1222: String or List required for argument %d"); | ||||
| static const char e_list_or_blob_required_for_argument_nr[] | ||||
|   = N_("E1226: List or Blob required for argument %d"); | ||||
| static const char e_list_or_dict_required_for_argument_nr[] | ||||
|   = N_("E1227: List or Dictionary required for argument %d"); | ||||
| static const char e_blob_required_for_argument_nr[] | ||||
|   = N_("E1238: Blob required for argument %d"); | ||||
| static const char e_invalid_value_for_blob_nr[] | ||||
| @@ -782,6 +792,27 @@ void tv_list_flatten(list_T *list, listitem_T *first, int64_t maxitems, int64_t | ||||
|   } | ||||
| } | ||||
|  | ||||
| /// "items(list)" function | ||||
| /// Caller must have already checked that argvars[0] is a List. | ||||
| static void tv_list2items(typval_T *argvars, typval_T *rettv) | ||||
| { | ||||
|   list_T *l = argvars[0].vval.v_list; | ||||
|  | ||||
|   tv_list_alloc_ret(rettv, tv_list_len(l)); | ||||
|   if (l == NULL) { | ||||
|     return;  // null list behaves like an empty list | ||||
|   } | ||||
|  | ||||
|   varnumber_T idx = 0; | ||||
|   TV_LIST_ITER(l, li, { | ||||
|     list_T *l2 = tv_list_alloc(2); | ||||
|     tv_list_append_list(rettv->vval.v_list, l2); | ||||
|     tv_list_append_number(l2, idx); | ||||
|     tv_list_append_tv(l2, TV_LIST_ITEM_TV(li)); | ||||
|     idx++; | ||||
|   }); | ||||
| } | ||||
|  | ||||
| /// Extend first list with the second | ||||
| /// | ||||
| /// @param[out]  l1  List to extend. | ||||
| @@ -3134,35 +3165,38 @@ void tv_dict_alloc_ret(typval_T *const ret_tv) | ||||
|  | ||||
| /// Turn a dictionary into a list | ||||
| /// | ||||
| /// @param[in] tv      Dictionary to convert. Is checked for actually being | ||||
| /// @param[in] argvars Arguments to items(). The first argument is check for being | ||||
| ///                    a dictionary, will give an error if not. | ||||
| /// @param[out] rettv  Location where result will be saved. | ||||
| /// @param[in] what    What to save in rettv. | ||||
| static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictListType what) | ||||
| static void tv_dict2list(typval_T *const argvars, typval_T *const rettv, const DictListType what) | ||||
| { | ||||
|   if (tv->v_type != VAR_DICT) { | ||||
|     emsg(_(e_dictreq)); | ||||
|   if ((what == kDict2ListItems | ||||
|        ? tv_check_for_list_or_dict_arg(argvars, 0) | ||||
|        : tv_check_for_dict_arg(argvars, 0)) == FAIL) { | ||||
|     tv_list_alloc_ret(rettv, 0); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict)); | ||||
|   if (tv->vval.v_dict == NULL) { | ||||
|   dict_T *d = argvars[0].vval.v_dict; | ||||
|   tv_list_alloc_ret(rettv, tv_dict_len(d)); | ||||
|   if (d == NULL) { | ||||
|     // NULL dict behaves like an empty dict | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   TV_DICT_ITER(tv->vval.v_dict, di, { | ||||
|   TV_DICT_ITER(d, di, { | ||||
|     typval_T tv_item = { .v_lock = VAR_UNLOCKED }; | ||||
|  | ||||
|     switch (what) { | ||||
|       case kDictListKeys: | ||||
|       case kDict2ListKeys: | ||||
|         tv_item.v_type = VAR_STRING; | ||||
|         tv_item.vval.v_string = xstrdup(di->di_key); | ||||
|         break; | ||||
|       case kDictListValues: | ||||
|       case kDict2ListValues: | ||||
|         tv_copy(&di->di_tv, &tv_item); | ||||
|         break; | ||||
|       case kDictListItems: { | ||||
|       case kDict2ListItems: { | ||||
|         // items() | ||||
|         list_T *const sub_l = tv_list_alloc(2); | ||||
|         tv_item.v_type = VAR_LIST; | ||||
| @@ -3188,19 +3222,23 @@ static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictLi | ||||
| /// "items(dict)" function | ||||
| void f_items(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) | ||||
| { | ||||
|   tv_dict_list(argvars, rettv, 2); | ||||
|   if (argvars[0].v_type == VAR_LIST) { | ||||
|     tv_list2items(argvars, rettv); | ||||
|   } else { | ||||
|     tv_dict2list(argvars, rettv, kDict2ListItems); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /// "keys()" function | ||||
| void f_keys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) | ||||
| { | ||||
|   tv_dict_list(argvars, rettv, 0); | ||||
|   tv_dict2list(argvars, rettv, kDict2ListKeys); | ||||
| } | ||||
|  | ||||
| /// "values(dict)" function | ||||
| void f_values(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) | ||||
| { | ||||
|   tv_dict_list(argvars, rettv, 1); | ||||
|   tv_dict2list(argvars, rettv, kDict2ListValues); | ||||
| } | ||||
|  | ||||
| /// "has_key()" function | ||||
| @@ -4398,6 +4436,17 @@ int tv_check_for_opt_string_or_list_arg(const typval_T *const args, const int id | ||||
|           || tv_check_for_string_or_list_arg(args, idx) != FAIL) ? OK : FAIL; | ||||
| } | ||||
|  | ||||
| /// Give an error and return FAIL unless "args[idx]" is a list or dict | ||||
| int tv_check_for_list_or_dict_arg(const typval_T *const args, const int idx) | ||||
|   FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE | ||||
| { | ||||
|   if (args[idx].v_type != VAR_LIST && args[idx].v_type != VAR_DICT) { | ||||
|     semsg(_(e_list_or_dict_required_for_argument_nr), idx + 1); | ||||
|     return FAIL; | ||||
|   } | ||||
|   return OK; | ||||
| } | ||||
|  | ||||
| /// Give an error and return FAIL unless "args[idx]" is a string | ||||
| /// or a function reference. | ||||
| int tv_check_for_string_or_func_arg(const typval_T *const args, const int idx) | ||||
|   | ||||
| @@ -195,6 +195,17 @@ func Test_list_range_assign() | ||||
|   call CheckDefAndScriptFailure(lines, 'E1012:', 2) | ||||
| endfunc | ||||
|  | ||||
| func Test_list_items() | ||||
|   let r = [] | ||||
|   let l = ['a', 'b', 'c'] | ||||
|   for [idx, val] in items(l) | ||||
|     call extend(r, [[idx, val]]) | ||||
|   endfor | ||||
|   call assert_equal([[0, 'a'], [1, 'b'], [2, 'c']], r) | ||||
|  | ||||
|   call assert_fails('call items(3)', 'E1227:') | ||||
| endfunc | ||||
|  | ||||
| " Test removing items in list | ||||
| func Test_list_func_remove() | ||||
|   let lines =<< trim END | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 zeertzjq
					zeertzjq