aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2024-07-30 11:34:38 +0800
committerzeertzjq <zeertzjq@outlook.com>2024-07-30 12:18:44 +0800
commit8ca3c1515c3fd5f70e870e535649a4c2afc5bbaf (patch)
treeb1a04b07fc123ffb82b9fc98a50f4ffac05ba25d
parent2dd0a90f211268517af30607c8eade0fef533edb (diff)
downloadrneovim-8ca3c1515c3fd5f70e870e535649a4c2afc5bbaf.tar.gz
rneovim-8ca3c1515c3fd5f70e870e535649a4c2afc5bbaf.tar.bz2
rneovim-8ca3c1515c3fd5f70e870e535649a4c2afc5bbaf.zip
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 <Bram@vim.org>
-rw-r--r--src/nvim/eval/typval.c53
-rw-r--r--test/old/testdir/test_listdict.vim11
2 files changed, 47 insertions, 17 deletions
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;
diff --git a/test/old/testdir/test_listdict.vim b/test/old/testdir/test_listdict.vim
index e976dc4c29..5e4a3fd1f8 100644
--- a/test/old/testdir/test_listdict.vim
+++ b/test/old/testdir/test_listdict.vim
@@ -203,7 +203,16 @@ func Test_list_items()
endfor
call assert_equal([[0, 'a'], [1, 'b'], [2, 'c']], r)
- call assert_fails('call items(3)', 'E1227:')
+ call assert_fails('call items(3)', 'E1225:')
+endfunc
+
+func Test_string_items()
+ let r = []
+ let s = 'ábツ'
+ for [idx, val] in items(s)
+ call extend(r, [[idx, val]])
+ endfor
+ call assert_equal([[0, 'á'], [1, 'b'], [2, 'ツ']], r)
endfunc
" Test removing items in list