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 |   EXPR_ISNOT,         ///< isnot | ||||||
| } exprtype_T; | } 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. | // Used for checking if local variables or arguments used in a lambda. | ||||||
| extern bool *eval_lavars_used; | extern bool *eval_lavars_used; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ | |||||||
| #include "nvim/eval/executor.h" | #include "nvim/eval/executor.h" | ||||||
| #include "nvim/eval/gc.h" | #include "nvim/eval/gc.h" | ||||||
| #include "nvim/eval/typval.h" | #include "nvim/eval/typval.h" | ||||||
|  | #include "nvim/eval/typval_defs.h" | ||||||
| #include "nvim/eval/typval_encode.h" | #include "nvim/eval/typval_encode.h" | ||||||
| #include "nvim/eval/userfunc.h" | #include "nvim/eval/userfunc.h" | ||||||
| #include "nvim/eval/vars.h" | #include "nvim/eval/vars.h" | ||||||
| @@ -59,6 +60,13 @@ typedef struct { | |||||||
|  |  | ||||||
| typedef int (*ListSorter)(const void *, const void *); | 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 | #ifdef INCLUDE_GENERATED_DECLARATIONS | ||||||
| # include "eval/typval.c.generated.h" | # include "eval/typval.c.generated.h" | ||||||
| #endif | #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"); |   = N_("E1222: String or List required for argument %d"); | ||||||
| static const char e_list_or_blob_required_for_argument_nr[] | static const char e_list_or_blob_required_for_argument_nr[] | ||||||
|   = N_("E1226: List or Blob required for argument %d"); |   = 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[] | static const char e_blob_required_for_argument_nr[] | ||||||
|   = N_("E1238: Blob required for argument %d"); |   = N_("E1238: Blob required for argument %d"); | ||||||
| static const char e_invalid_value_for_blob_nr[] | 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 | /// Extend first list with the second | ||||||
| /// | /// | ||||||
| /// @param[out]  l1  List to extend. | /// @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 | /// 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. | ///                    a dictionary, will give an error if not. | ||||||
| /// @param[out] rettv  Location where result will be saved. | /// @param[out] rettv  Location where result will be saved. | ||||||
| /// @param[in] what    What to save in rettv. | /// @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) { |   if ((what == kDict2ListItems | ||||||
|     emsg(_(e_dictreq)); |        ? tv_check_for_list_or_dict_arg(argvars, 0) | ||||||
|  |        : tv_check_for_dict_arg(argvars, 0)) == FAIL) { | ||||||
|  |     tv_list_alloc_ret(rettv, 0); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict)); |   dict_T *d = argvars[0].vval.v_dict; | ||||||
|   if (tv->vval.v_dict == NULL) { |   tv_list_alloc_ret(rettv, tv_dict_len(d)); | ||||||
|  |   if (d == NULL) { | ||||||
|     // NULL dict behaves like an empty dict |     // NULL dict behaves like an empty dict | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   TV_DICT_ITER(tv->vval.v_dict, di, { |   TV_DICT_ITER(d, di, { | ||||||
|     typval_T tv_item = { .v_lock = VAR_UNLOCKED }; |     typval_T tv_item = { .v_lock = VAR_UNLOCKED }; | ||||||
|  |  | ||||||
|     switch (what) { |     switch (what) { | ||||||
|       case kDictListKeys: |       case kDict2ListKeys: | ||||||
|         tv_item.v_type = VAR_STRING; |         tv_item.v_type = VAR_STRING; | ||||||
|         tv_item.vval.v_string = xstrdup(di->di_key); |         tv_item.vval.v_string = xstrdup(di->di_key); | ||||||
|         break; |         break; | ||||||
|       case kDictListValues: |       case kDict2ListValues: | ||||||
|         tv_copy(&di->di_tv, &tv_item); |         tv_copy(&di->di_tv, &tv_item); | ||||||
|         break; |         break; | ||||||
|       case kDictListItems: { |       case kDict2ListItems: { | ||||||
|         // items() |         // items() | ||||||
|         list_T *const sub_l = tv_list_alloc(2); |         list_T *const sub_l = tv_list_alloc(2); | ||||||
|         tv_item.v_type = VAR_LIST; |         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 | /// "items(dict)" function | ||||||
| void f_items(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) | 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 | /// "keys()" function | ||||||
| void f_keys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) | 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 | /// "values(dict)" function | ||||||
| void f_values(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) | 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 | /// "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; |           || 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 | /// Give an error and return FAIL unless "args[idx]" is a string | ||||||
| /// or a function reference. | /// or a function reference. | ||||||
| int tv_check_for_string_or_func_arg(const typval_T *const args, const int idx) | 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) |   call CheckDefAndScriptFailure(lines, 'E1012:', 2) | ||||||
| endfunc | 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 | " Test removing items in list | ||||||
| func Test_list_func_remove() | func Test_list_func_remove() | ||||||
|   let lines =<< trim END |   let lines =<< trim END | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 zeertzjq
					zeertzjq