mirror of
https://github.com/neovim/neovim.git
synced 2026-03-31 21:02:11 +00:00
vim-patch:partial:9.1.1668: items() does not work for Blobs
Problem: items() does not work for Blobs
Solution: Extend items() to support Blob
(Yegappan Lakshmanan).
closes: vim/vim#18080
da34f84847
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
@@ -745,6 +745,7 @@ Blob manipulation: *blob-functions*
|
||||
reverse() reverse the order of numbers in a blob
|
||||
index() index of a value in a Blob
|
||||
indexof() index in a Blob where an expression is true
|
||||
items() get List of Blob index-value pairs
|
||||
|
||||
Other computation: *bitwise-function*
|
||||
and() bitwise AND
|
||||
|
||||
@@ -5415,7 +5415,7 @@ items({expr}) *items()*
|
||||
Return a |List| with all key/index and value pairs of {expr}.
|
||||
Each |List| item is a list with two items:
|
||||
- for a |Dict|: the key and the value
|
||||
- for a |List| or |String|: the index and the value
|
||||
- for a |List|, |Blob| or |String|: the index and the value
|
||||
The returned |List| is in arbitrary order for a |Dict|,
|
||||
otherwise it's in ascending order of the index.
|
||||
|
||||
@@ -5428,6 +5428,7 @@ items({expr}) *items()*
|
||||
endfor
|
||||
echo items([1, 2, 3])
|
||||
echo items("foobar")
|
||||
echo items(0z0102)
|
||||
<
|
||||
|
||||
Parameters: ~
|
||||
|
||||
3
runtime/lua/vim/_meta/vimfn.lua
generated
3
runtime/lua/vim/_meta/vimfn.lua
generated
@@ -4901,7 +4901,7 @@ function vim.fn.isnan(expr) end
|
||||
--- Return a |List| with all key/index and value pairs of {expr}.
|
||||
--- Each |List| item is a list with two items:
|
||||
--- - for a |Dict|: the key and the value
|
||||
--- - for a |List| or |String|: the index and the value
|
||||
--- - for a |List|, |Blob| or |String|: the index and the value
|
||||
--- The returned |List| is in arbitrary order for a |Dict|,
|
||||
--- otherwise it's in ascending order of the index.
|
||||
---
|
||||
@@ -4914,6 +4914,7 @@ function vim.fn.isnan(expr) end
|
||||
--- endfor
|
||||
--- echo items([1, 2, 3])
|
||||
--- echo items("foobar")
|
||||
--- echo items(0z0102)
|
||||
--- <
|
||||
---
|
||||
--- @param expr table|string
|
||||
|
||||
@@ -6044,7 +6044,7 @@ M.funcs = {
|
||||
Return a |List| with all key/index and value pairs of {expr}.
|
||||
Each |List| item is a list with two items:
|
||||
- for a |Dict|: the key and the value
|
||||
- for a |List| or |String|: the index and the value
|
||||
- for a |List|, |Blob| or |String|: the index and the value
|
||||
The returned |List| is in arbitrary order for a |Dict|,
|
||||
otherwise it's in ascending order of the index.
|
||||
|
||||
@@ -6057,6 +6057,7 @@ M.funcs = {
|
||||
endfor
|
||||
echo items([1, 2, 3])
|
||||
echo items("foobar")
|
||||
echo items(0z0102)
|
||||
<
|
||||
]=],
|
||||
name = 'items',
|
||||
|
||||
@@ -93,8 +93,8 @@ 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_dict_blob_or_string_required_for_argument_nr[]
|
||||
= N_("E1225: List, Dictionary, Blob or String 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_blob_required_for_argument_nr[]
|
||||
@@ -793,6 +793,30 @@ void tv_list_flatten(list_T *list, listitem_T *first, int64_t maxitems, int64_t
|
||||
}
|
||||
}
|
||||
|
||||
/// "items(blob)" function
|
||||
/// Converts a Blob into a List of [index, byte] pairs.
|
||||
/// Caller must have already checked that argvars[0] is a Blob.
|
||||
/// A null blob behaves like an empty blob.
|
||||
static void tv_blob2items(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
blob_T *blob = argvars[0].vval.v_blob;
|
||||
|
||||
tv_list_alloc_ret(rettv, tv_blob_len(blob));
|
||||
|
||||
for (int i = 0; i < tv_blob_len(blob); i++) {
|
||||
list_T *l2 = tv_list_alloc(2);
|
||||
tv_list_append_list(rettv->vval.v_list, l2);
|
||||
tv_list_append_number(l2, i);
|
||||
tv_list_append_number(l2, tv_blob_get(blob, i));
|
||||
}
|
||||
}
|
||||
|
||||
/// "items(dict)" function
|
||||
static void tv_dict2items(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
tv_dict2list(argvars, rettv, kDict2ListItems);
|
||||
}
|
||||
|
||||
/// "items(list)" function
|
||||
/// Caller must have already checked that argvars[0] is a List.
|
||||
static void tv_list2items(typval_T *argvars, typval_T *rettv)
|
||||
@@ -3226,9 +3250,7 @@ void tv_dict_alloc_ret(typval_T *const ret_tv)
|
||||
/// @param[in] what What to save in rettv.
|
||||
static void tv_dict2list(typval_T *const argvars, typval_T *const rettv, const DictListType what)
|
||||
{
|
||||
if ((what == kDict2ListItems
|
||||
? tv_check_for_string_or_list_or_dict_arg(argvars, 0)
|
||||
: tv_check_for_dict_arg(argvars, 0)) == FAIL) {
|
||||
if (tv_check_for_dict_arg(argvars, 0) == FAIL) {
|
||||
tv_list_alloc_ret(rettv, 0);
|
||||
return;
|
||||
}
|
||||
@@ -3267,15 +3289,19 @@ static void tv_dict2list(typval_T *const argvars, typval_T *const rettv, const D
|
||||
});
|
||||
}
|
||||
|
||||
/// "items(dict)" function
|
||||
/// "items()" function
|
||||
void f_items(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
if (argvars[0].v_type == VAR_STRING) {
|
||||
tv_string2items(argvars, rettv);
|
||||
} else if (argvars[0].v_type == VAR_LIST) {
|
||||
tv_list2items(argvars, rettv);
|
||||
} else if (argvars[0].v_type == VAR_BLOB) {
|
||||
tv_blob2items(argvars, rettv);
|
||||
} else if (argvars[0].v_type == VAR_DICT) {
|
||||
tv_dict2items(argvars, rettv);
|
||||
} else {
|
||||
tv_dict2list(argvars, rettv, kDict2ListItems);
|
||||
semsg(_(e_list_dict_blob_or_string_required_for_argument_nr), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4511,19 +4537,6 @@ 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 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
|
||||
/// or a function reference.
|
||||
int tv_check_for_string_or_func_arg(const typval_T *const args, const int idx)
|
||||
|
||||
@@ -884,4 +884,15 @@ func Test_indexof()
|
||||
call assert_fails('let i = indexof(b, " ")', 'E15:')
|
||||
endfunc
|
||||
|
||||
" Test for using the items() function with a blob
|
||||
func Test_blob_items()
|
||||
let lines =<< trim END
|
||||
call assert_equal([[0, 0xAA], [1, 0xBB], [2, 0xCC]], 0zAABBCC->items())
|
||||
call assert_equal([[0, 0]], 0z00->items())
|
||||
call assert_equal([], 0z->items())
|
||||
call assert_equal([], v:_null_blob->items())
|
||||
END
|
||||
call CheckSourceLegacyAndVim9Success(lines)
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
||||
@@ -89,20 +89,24 @@ func CheckLegacyFailure(lines, error)
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
" Translate "lines" to legacy Vim script
|
||||
func s:LegacyTrans(lines)
|
||||
return a:lines->mapnew({_, v ->
|
||||
\ v->substitute('\<VAR\>', 'let', 'g')
|
||||
\ ->substitute('\<LET\>', 'let', 'g')
|
||||
\ ->substitute('\<LSTART\>', '{', 'g')
|
||||
\ ->substitute('\<LMIDDLE\>', '->', 'g')
|
||||
\ ->substitute('\<LEND\>', '}', 'g')
|
||||
\ ->substitute('\<TRUE\>', '1', 'g')
|
||||
\ ->substitute('\<FALSE\>', '0', 'g')
|
||||
\ ->substitute('#"', ' "', 'g')
|
||||
\ })
|
||||
endfunc
|
||||
|
||||
" Execute "lines" in a legacy function, translated as in
|
||||
" CheckLegacyAndVim9Success()
|
||||
func CheckTransLegacySuccess(lines)
|
||||
let legacylines = a:lines->mapnew({_, v ->
|
||||
\ v->substitute('\<VAR\>', 'let', 'g')
|
||||
\ ->substitute('\<LET\>', 'let', 'g')
|
||||
\ ->substitute('\<LSTART\>', '{', 'g')
|
||||
\ ->substitute('\<LMIDDLE\>', '->', 'g')
|
||||
\ ->substitute('\<LEND\>', '}', 'g')
|
||||
\ ->substitute('\<TRUE\>', '1', 'g')
|
||||
\ ->substitute('\<FALSE\>', '0', 'g')
|
||||
\ ->substitute('#"', ' "', 'g')
|
||||
\ })
|
||||
call CheckLegacySuccess(legacylines)
|
||||
call CheckLegacySuccess(s:LegacyTrans(a:lines))
|
||||
endfunc
|
||||
|
||||
func CheckTransDefSuccess(lines)
|
||||
@@ -143,6 +147,65 @@ func CheckLegacyAndVim9Failure(lines, error)
|
||||
call CheckLegacyFailure(legacylines, legacyError)
|
||||
endfunc
|
||||
|
||||
" Check that "lines" inside a legacy function has no error.
|
||||
func CheckSourceLegacySuccess(lines)
|
||||
let cwd = getcwd()
|
||||
new
|
||||
call setline(1, ['func Func()'] + a:lines + ['endfunc', 'call Func()'])
|
||||
let bnr = bufnr()
|
||||
try
|
||||
:source
|
||||
finally
|
||||
delfunc! Func
|
||||
call chdir(cwd)
|
||||
exe $':bw! {bnr}'
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
" Check that "lines" inside a legacy function results in the expected error
|
||||
func CheckSourceLegacyFailure(lines, error)
|
||||
let cwd = getcwd()
|
||||
new
|
||||
call setline(1, ['func Func()'] + a:lines + ['endfunc', 'call Func()'])
|
||||
let bnr = bufnr()
|
||||
try
|
||||
call assert_fails('source', a:error)
|
||||
finally
|
||||
delfunc! Func
|
||||
call chdir(cwd)
|
||||
exe $':bw! {bnr}'
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
" Execute "lines" in a legacy function, translated as in
|
||||
" CheckSourceLegacyAndVim9Success()
|
||||
func CheckSourceTransLegacySuccess(lines)
|
||||
call CheckSourceLegacySuccess(s:LegacyTrans(a:lines))
|
||||
endfunc
|
||||
|
||||
" Execute "lines" in a :def function, translated as in
|
||||
" CheckLegacyAndVim9Success()
|
||||
func CheckSourceTransDefSuccess(lines)
|
||||
return
|
||||
endfunc
|
||||
|
||||
" Execute "lines" in a Vim9 script, translated as in
|
||||
" CheckLegacyAndVim9Success()
|
||||
func CheckSourceTransVim9Success(lines)
|
||||
return
|
||||
endfunc
|
||||
|
||||
" Execute "lines" in a legacy function, :def function and Vim9 script.
|
||||
" Use 'VAR' for a declaration.
|
||||
" Use 'LET' for an assignment
|
||||
" Use ' #"' for a comment
|
||||
" Use LSTART arg LMIDDLE expr LEND for lambda
|
||||
" Use 'TRUE' for 1 in legacy, true in Vim9
|
||||
" Use 'FALSE' for 0 in legacy, false in Vim9
|
||||
func CheckSourceLegacyAndVim9Success(lines)
|
||||
call CheckSourceTransLegacySuccess(a:lines)
|
||||
endfunc
|
||||
|
||||
" :source a list of "lines" and check whether it fails with "error"
|
||||
func CheckSourceScriptFailure(lines, error, lnum = -3)
|
||||
if get(a:lines, 0, '') ==# 'vim9script'
|
||||
@@ -195,26 +258,10 @@ func CheckSourceScriptSuccess(lines)
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
func CheckSourceSuccess(lines)
|
||||
call CheckSourceScriptSuccess(a:lines)
|
||||
endfunc
|
||||
|
||||
func CheckSourceFailure(lines, error, lnum = -3)
|
||||
call CheckSourceScriptFailure(a:lines, a:error, a:lnum)
|
||||
endfunc
|
||||
|
||||
func CheckSourceFailureList(lines, errors, lnum = -3)
|
||||
call CheckSourceScriptFailureList(a:lines, a:errors, a:lnum)
|
||||
endfunc
|
||||
|
||||
func CheckSourceDefSuccess(lines)
|
||||
return
|
||||
endfunc
|
||||
|
||||
func CheckSourceDefAndScriptSuccess(lines)
|
||||
return
|
||||
endfunc
|
||||
|
||||
func CheckSourceDefCompileSuccess(lines)
|
||||
return
|
||||
endfunc
|
||||
@@ -235,3 +282,28 @@ func CheckSourceDefExecAndScriptFailure(lines, error, lnum = -3)
|
||||
return
|
||||
endfunc
|
||||
|
||||
func CheckSourceSuccess(lines)
|
||||
call CheckSourceScriptSuccess(a:lines)
|
||||
endfunc
|
||||
|
||||
func CheckSourceFailure(lines, error, lnum = -3)
|
||||
call CheckSourceScriptFailure(a:lines, a:error, a:lnum)
|
||||
endfunc
|
||||
|
||||
func CheckSourceFailureList(lines, errors, lnum = -3)
|
||||
call CheckSourceScriptFailureList(a:lines, a:errors, a:lnum)
|
||||
endfunc
|
||||
|
||||
func CheckSourceDefAndScriptSuccess(lines)
|
||||
return
|
||||
endfunc
|
||||
|
||||
" Execute "lines" in a legacy function, :def function and Vim9 script.
|
||||
" Use 'VAR' for a declaration.
|
||||
" Use 'LET' for an assignment
|
||||
" Use ' #"' for a comment
|
||||
func CheckSourceLegacyAndVim9Failure(lines, error)
|
||||
let legacyError = type(a:error) == type('string') ? a:error : a:error[0]
|
||||
call CheckSourceLegacyFailure(s:LegacyTrans(a:lines), legacyError)
|
||||
endfunc
|
||||
|
||||
|
||||
Reference in New Issue
Block a user