From 96b358e9f138e60a24c3f3c9b45e9b82ffb92c1c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 30 Jul 2024 11:08:16 +0800 Subject: 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) https://github.com/vim/vim/commit/976f859763b215050a03248dbc2bb62fa5d0d059 Skip CHECK_LIST_MATERIALIZE. Co-authored-by: Bram Moolenaar --- src/nvim/eval.h | 7 ----- src/nvim/eval/typval.c | 75 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 62 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.h b/src/nvim/eval.h index d83af70ef7..004fc82222 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -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; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 0d6de3c3e5..8fc8dcc479 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -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) -- cgit From 8ca3c1515c3fd5f70e870e535649a4c2afc5bbaf Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 30 Jul 2024 11:34:38 +0800 Subject: vim-patch:9.0.0331: cannot use items() on a string Problem: Cannot use items() on a string. Solution: Make items() work on a string. (closes vim/vim#11016) https://github.com/vim/vim/commit/3e518a8ec74065aedd67d352c93d6ae6be550316 Co-authored-by: Bram Moolenaar --- src/nvim/eval/typval.c | 53 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 8fc8dcc479..4458dba27d 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -93,10 +93,10 @@ static const char e_string_or_number_required_for_argument_nr[] = N_("E1220: String or Number required for argument %d"); static const char e_string_or_list_required_for_argument_nr[] = N_("E1222: String or List required for argument %d"); +static const char e_string_list_or_dict_required_for_argument_nr[] + = N_("E1225: String, List or Dictionary 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[] @@ -813,6 +813,30 @@ static void tv_list2items(typval_T *argvars, typval_T *rettv) }); } +/// "items(string)" function +/// Caller must have already checked that argvars[0] is a String. +static void tv_string2items(typval_T *argvars, typval_T *rettv) +{ + const char *p = argvars[0].vval.v_string; + + tv_list_alloc_ret(rettv, kListLenMayKnow); + if (p == NULL) { + return; // null string behaves like an empty string + } + + for (varnumber_T idx = 0; *p != NUL; idx++) { + int len = utfc_ptr2len(p); + if (len == 0) { + break; + } + 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_string(l2, p, len); + p += len; + } +} + /// Extend first list with the second /// /// @param[out] l1 List to extend. @@ -3172,7 +3196,7 @@ void tv_dict_alloc_ret(typval_T *const ret_tv) static void tv_dict2list(typval_T *const argvars, typval_T *const rettv, const DictListType what) { if ((what == kDict2ListItems - ? tv_check_for_list_or_dict_arg(argvars, 0) + ? tv_check_for_string_or_list_or_dict_arg(argvars, 0) : tv_check_for_dict_arg(argvars, 0)) == FAIL) { tv_list_alloc_ret(rettv, 0); return; @@ -3202,15 +3226,8 @@ static void tv_dict2list(typval_T *const argvars, typval_T *const rettv, const D tv_item.v_type = VAR_LIST; tv_item.vval.v_list = sub_l; tv_list_ref(sub_l); - - tv_list_append_owned_tv(sub_l, (typval_T) { - .v_type = VAR_STRING, - .v_lock = VAR_UNLOCKED, - .vval.v_string = xstrdup(di->di_key), - }); - + tv_list_append_string(sub_l, di->di_key, -1); tv_list_append_tv(sub_l, &di->di_tv); - break; } } @@ -3222,7 +3239,9 @@ static void tv_dict2list(typval_T *const argvars, typval_T *const rettv, const D /// "items(dict)" function void f_items(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - if (argvars[0].v_type == VAR_LIST) { + if (argvars[0].v_type == VAR_STRING) { + tv_string2items(argvars, rettv); + } else if (argvars[0].v_type == VAR_LIST) { tv_list2items(argvars, rettv); } else { tv_dict2list(argvars, rettv, kDict2ListItems); @@ -4436,12 +4455,14 @@ 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) +/// Give an error and return FAIL unless "args[idx]" is a string or a list or a dict +int tv_check_for_string_or_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); + if (args[idx].v_type != VAR_STRING + && args[idx].v_type != VAR_LIST + && args[idx].v_type != VAR_DICT) { + semsg(_(e_string_list_or_dict_required_for_argument_nr), idx + 1); return FAIL; } return OK; -- cgit From 0af056ebce189deedb58f7048108977fa5d81713 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 30 Jul 2024 11:46:24 +0800 Subject: vim-patch:49cdd62: runtime(doc): list of new/changed features in version9.txt closes: vim/vim#13753 https://github.com/vim/vim/commit/49cdd629a39d7e40e7349e65cb177e2442871a04 Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 4bc0827bcc..687d052d19 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -5752,7 +5752,10 @@ M.funcs = { for [key, value] in items(mydict) echo key .. ': ' .. value endfor - + < + A List or a String argument is also supported. In these + cases, items() returns a List with the index and the value at + the index. ]=], name = 'items', params = { { 'dict', 'any' } }, -- cgit