fix(eval): partially port v8.2.3284

These were issues that I found while porting that I fixed upstream. :^)

Very little of the patch can be exactly ported as we're a bit behind on
dependant patches (we also can't use the exact :for emsg, as we don't
support iterating over Strings yet), so just translate the fixes as best
as we can for now.

Include latest relevant doc changes from:
- v8.1.0815
- v8.2.2658
This commit is contained in:
Sean Dewar 2021-08-01 18:27:41 +01:00
parent 3d6bb8b3fb
commit e88961943b
No known key found for this signature in database
GPG Key ID: 08CC2C83AD41B581
5 changed files with 67 additions and 26 deletions

View File

@ -372,8 +372,8 @@ Changing the order of items in a list: >
For loop ~
The |:for| loop executes commands for each item in a list. A variable is set
to each item in the list in sequence. Example: >
The |:for| loop executes commands for each item in a |List| or |Blob|.
A variable is set to each item in the sequence. Example with a List: >
:for item in mylist
: call Doit(item)
:endfor
@ -406,6 +406,8 @@ It is also possible to put remaining items in a List variable: >
: endif
:endfor
For a Blob one byte at a time is used.
List functions ~
*E714*
@ -637,6 +639,16 @@ is not available it returns -1 or the default value you specify: >
:echo get(myblob, idx, 999)
Blob iteration ~
The |:for| loop executes commands for each byte of a Blob. The loop variable is
set to each byte in the Blob. Example: >
:for byte in 0z112233
: call Doit(byte)
:endfor
This calls Doit() with 0x11, 0x22 and 0x33.
Blob concatenation ~
Two blobs can be concatenated with the "+" operator: >
@ -10999,28 +11011,34 @@ text...
NOTE: The ":append" and ":insert" commands don't work
properly inside a ":while" and ":for" loop.
:for {var} in {list} *:for* *E690* *E732*
:for {var} in {object} *:for* *E690* *E732*
:endfo[r] *:endfo* *:endfor*
Repeat the commands between ":for" and ":endfor" for
each item in {list}. Variable {var} is set to the
value of each item.
When an error is detected for a command inside the
loop, execution continues after the "endfor".
Changing {list} inside the loop affects what items are
used. Make a copy if this is unwanted: >
each item in {object}. {object} can be a |List| or
a |Blob|. Variable {var} is set to the value of each
item. When an error is detected for a command inside
the loop, execution continues after the "endfor".
Changing {object} inside the loop affects what items
are used. Make a copy if this is unwanted: >
:for item in copy(mylist)
< When not making a copy, Vim stores a reference to the
next item in the list, before executing the commands
with the current item. Thus the current item can be
removed without effect. Removing any later item means
it will not be found. Thus the following example
works (an inefficient way to make a list empty): >
<
When {object} is a |List| and not making a copy, Vim
stores a reference to the next item in the |List|
before executing the commands with the current item.
Thus the current item can be removed without effect.
Removing any later item means it will not be found.
Thus the following example works (an inefficient way
to make a |List| empty): >
for item in mylist
call remove(mylist, 0)
endfor
< Note that reordering the list (e.g., with sort() or
< Note that reordering the |List| (e.g., with sort() or
reverse()) may have unexpected effects.
When {object} is a |Blob|, Vim always makes a copy to
iterate over. Unlike with |List|, modifying the
|Blob| does not affect the iteration.
:for [{var1}, {var2}, ...] in {listlist}
:endfo[r]
Like ":for" above, but each item in {listlist} must be

View File

@ -2614,7 +2614,7 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip)
}
tv_clear(&tv);
} else {
EMSG(_(e_listreq));
EMSG(_(e_listblobreq));
tv_clear(&tv);
}
}

View File

@ -4984,12 +4984,16 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool error = false;
if (argvars[0].v_type == VAR_BLOB) {
if (argvars[0].vval.v_blob == NULL) {
blob_T *const b = argvars[0].vval.v_blob;
if (b == NULL
|| var_check_lock(b->bv_lock, N_("insert() argument"),
TV_TRANSLATE)) {
return;
}
long before = 0;
const int len = tv_blob_len(argvars[0].vval.v_blob);
const int len = tv_blob_len(b);
if (argvars[2].v_type != VAR_UNKNOWN) {
before = (long)tv_get_number_chk(&argvars[2], &error);
@ -5010,11 +5014,11 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
ga_grow(&argvars[0].vval.v_blob->bv_ga, 1);
char_u *const p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data;
ga_grow(&b->bv_ga, 1);
char_u *const p = (char_u *)b->bv_ga.ga_data;
memmove(p + before + 1, p + before, (size_t)len - before);
*(p + before) = val;
argvars[0].vval.v_blob->bv_ga.ga_len++;
b->bv_ga.ga_len++;
tv_copy(&argvars[0], rettv);
} else if (argvars[0].v_type != VAR_LIST) {
@ -7312,11 +7316,16 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
} else if (argvars[0].v_type == VAR_BLOB) {
blob_T *const b = argvars[0].vval.v_blob;
if (b != NULL && var_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) {
return;
}
bool error = false;
idx = (long)tv_get_number_chk(&argvars[1], &error);
if (!error) {
blob_T *const b = argvars[0].vval.v_blob;
const int len = tv_blob_len(b);
if (idx < 0) {

View File

@ -251,6 +251,12 @@ func Test_blob_func_remove()
call assert_fails("call remove(1, 0)", 'E896:')
call assert_fails("call remove(b, b)", 'E974:')
call assert_fails("call remove(v:_null_blob, 1, 2)", 'E979:')
" Translated from v8.2.3284
let b = 0zDEADBEEF
lockvar b
call assert_fails('call remove(b, 0)', 'E741:')
unlockvar b
endfunc
func Test_blob_read_write()
@ -308,6 +314,12 @@ func Test_blob_insert()
call assert_fails('call insert(b, 257)', 'E475:')
call assert_fails('call insert(b, 0, [9])', 'E745:')
call assert_equal(0, insert(v:_null_blob, 0x33))
" Translated from v8.2.3284
let b = 0zDEADBEEF
lockvar b
call assert_fails('call insert(b, 3)', 'E741:')
unlockvar b
endfunc
func Test_blob_reverse()

View File

@ -23,9 +23,11 @@ func Test_E963()
endfunc
func Test_for_invalid()
call assert_fails("for x in 99", 'E714:')
call assert_fails("for x in function('winnr')", 'E714:')
call assert_fails("for x in {'a': 9}", 'E714:')
" Vim gives incorrect emsg here until v8.2.3284, but the exact emsg from that
" patch cannot be used until v8.2.2658 is ported (for loop over Strings)
call assert_fails("for x in 99", 'E897:')
call assert_fails("for x in function('winnr')", 'E897:')
call assert_fails("for x in {'a': 9}", 'E897:')
if 0
/1/5/2/s/\n