mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06: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:
parent
cd46383667
commit
96b358e9f1
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user