mirror of
https://github.com/neovim/neovim.git
synced 2025-11-23 10:36:29 +00:00
Merge pull request #29912 from zeertzjq/vim-9.0.0327
vim-patch:9.0.{partial:0327,0330,0331,0333}
This commit is contained in:
4
runtime/doc/builtin.txt
generated
4
runtime/doc/builtin.txt
generated
@@ -3901,6 +3901,10 @@ items({dict}) *items()*
|
|||||||
for [key, value] in items(mydict)
|
for [key, value] in items(mydict)
|
||||||
echo key .. ': ' .. value
|
echo key .. ': ' .. value
|
||||||
endfor
|
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.
|
||||||
|
|
||||||
jobpid({job}) *jobpid()*
|
jobpid({job}) *jobpid()*
|
||||||
Return the PID (process id) of |job-id| {job}.
|
Return the PID (process id) of |job-id| {job}.
|
||||||
|
|||||||
@@ -354,7 +354,8 @@ Note:
|
|||||||
|
|
||||||
*E1255* *E1136*
|
*E1255* *E1136*
|
||||||
<Cmd> commands must terminate, that is, they must be followed by <CR> in the
|
<Cmd> commands must terminate, that is, they must be followed by <CR> in the
|
||||||
{rhs} of the mapping definition. |Command-line| mode is never entered.
|
{rhs} of the mapping definition. |Command-line| mode is never entered. To use
|
||||||
|
a literal <CR> in the {rhs}, use |<lt>|.
|
||||||
|
|
||||||
|
|
||||||
1.3 MAPPING AND MODES *:map-modes*
|
1.3 MAPPING AND MODES *:map-modes*
|
||||||
|
|||||||
4
runtime/lua/vim/_meta/vimfn.lua
generated
4
runtime/lua/vim/_meta/vimfn.lua
generated
@@ -4699,6 +4699,10 @@ function vim.fn.isnan(expr) end
|
|||||||
--- for [key, value] in items(mydict)
|
--- for [key, value] in items(mydict)
|
||||||
--- echo key .. ': ' .. value
|
--- echo key .. ': ' .. value
|
||||||
--- endfor
|
--- 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.
|
||||||
---
|
---
|
||||||
--- @param dict any
|
--- @param dict any
|
||||||
--- @return any
|
--- @return any
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -5752,7 +5752,10 @@ M.funcs = {
|
|||||||
for [key, value] in items(mydict)
|
for [key, value] in items(mydict)
|
||||||
echo key .. ': ' .. value
|
echo key .. ': ' .. value
|
||||||
endfor
|
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',
|
name = 'items',
|
||||||
params = { { 'dict', 'any' } },
|
params = { { 'dict', 'any' } },
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -85,6 +93,8 @@ static const char e_string_or_number_required_for_argument_nr[]
|
|||||||
= N_("E1220: String or Number required for argument %d");
|
= N_("E1220: String or Number required for argument %d");
|
||||||
static const char e_string_or_list_required_for_argument_nr[]
|
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_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[]
|
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_blob_required_for_argument_nr[]
|
static const char e_blob_required_for_argument_nr[]
|
||||||
@@ -782,6 +792,51 @@ 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++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "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
|
/// Extend first list with the second
|
||||||
///
|
///
|
||||||
/// @param[out] l1 List to extend.
|
/// @param[out] l1 List to extend.
|
||||||
@@ -3134,49 +3189,45 @@ 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_string_or_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;
|
||||||
tv_item.vval.v_list = sub_l;
|
tv_item.vval.v_list = sub_l;
|
||||||
tv_list_ref(sub_l);
|
tv_list_ref(sub_l);
|
||||||
|
tv_list_append_string(sub_l, di->di_key, -1);
|
||||||
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_tv(sub_l, &di->di_tv);
|
tv_list_append_tv(sub_l, &di->di_tv);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3188,19 +3239,25 @@ 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_STRING) {
|
||||||
|
tv_string2items(argvars, rettv);
|
||||||
|
} else 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 +4455,19 @@ 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 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_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;
|
||||||
|
}
|
||||||
|
|
||||||
/// 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,26 @@ 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)', '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
|
" Test removing items in list
|
||||||
func Test_list_func_remove()
|
func Test_list_func_remove()
|
||||||
let lines =<< trim END
|
let lines =<< trim END
|
||||||
|
|||||||
@@ -20,9 +20,8 @@ func Test_list_method()
|
|||||||
call assert_equal(2, l->get(1))
|
call assert_equal(2, l->get(1))
|
||||||
call assert_equal(1, l->index(2))
|
call assert_equal(1, l->index(2))
|
||||||
call assert_equal([0, 1, 2, 3], [1, 2, 3]->insert(0))
|
call assert_equal([0, 1, 2, 3], [1, 2, 3]->insert(0))
|
||||||
call assert_fails('eval l->items()', 'E715:')
|
|
||||||
call assert_equal('1 2 3', l->join())
|
call assert_equal('1 2 3', l->join())
|
||||||
call assert_fails('eval l->keys()', 'E715:')
|
call assert_fails('eval l->keys()', 'E1206:')
|
||||||
call assert_equal(3, l->len())
|
call assert_equal(3, l->len())
|
||||||
call assert_equal([2, 3, 4], [1, 2, 3]->map('v:val + 1'))
|
call assert_equal([2, 3, 4], [1, 2, 3]->map('v:val + 1'))
|
||||||
call assert_equal(3, l->max())
|
call assert_equal(3, l->max())
|
||||||
@@ -34,7 +33,7 @@ func Test_list_method()
|
|||||||
call assert_equal('[1, 2, 3]', l->string())
|
call assert_equal('[1, 2, 3]', l->string())
|
||||||
call assert_equal(v:t_list, l->type())
|
call assert_equal(v:t_list, l->type())
|
||||||
call assert_equal([1, 2, 3], [1, 1, 2, 3, 3]->uniq())
|
call assert_equal([1, 2, 3], [1, 1, 2, 3, 3]->uniq())
|
||||||
call assert_fails('eval l->values()', 'E715:')
|
call assert_fails('eval l->values()', 'E1206:')
|
||||||
call assert_fails('echo []->len', 'E107:')
|
call assert_fails('echo []->len', 'E107:')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
@@ -79,6 +78,7 @@ func Test_string_method()
|
|||||||
eval "a\rb\ec"->strtrans()->assert_equal('a^Mb^[c')
|
eval "a\rb\ec"->strtrans()->assert_equal('a^Mb^[c')
|
||||||
eval "aあb"->strwidth()->assert_equal(4)
|
eval "aあb"->strwidth()->assert_equal(4)
|
||||||
eval 'abc'->substitute('b', 'x', '')->assert_equal('axc')
|
eval 'abc'->substitute('b', 'x', '')->assert_equal('axc')
|
||||||
|
call assert_fails('eval 123->items()', 'E1225:')
|
||||||
|
|
||||||
eval 'abc'->printf('the %s arg')->assert_equal('the abc arg')
|
eval 'abc'->printf('the %s arg')->assert_equal('the abc arg')
|
||||||
endfunc
|
endfunc
|
||||||
|
|||||||
Reference in New Issue
Block a user