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)

3e518a8ec7

Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
zeertzjq
2024-07-30 11:34:38 +08:00
parent 2dd0a90f21
commit 8ca3c1515c
2 changed files with 47 additions and 17 deletions

View File

@@ -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;

View File

@@ -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