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 ~ For loop ~
The |:for| loop executes commands for each item in a list. A variable is set The |:for| loop executes commands for each item in a |List| or |Blob|.
to each item in the list in sequence. Example: > A variable is set to each item in the sequence. Example with a List: >
:for item in mylist :for item in mylist
: call Doit(item) : call Doit(item)
:endfor :endfor
@ -406,6 +406,8 @@ It is also possible to put remaining items in a List variable: >
: endif : endif
:endfor :endfor
For a Blob one byte at a time is used.
List functions ~ List functions ~
*E714* *E714*
@ -637,6 +639,16 @@ is not available it returns -1 or the default value you specify: >
:echo get(myblob, idx, 999) :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 ~ Blob concatenation ~
Two blobs can be concatenated with the "+" operator: > Two blobs can be concatenated with the "+" operator: >
@ -10999,28 +11011,34 @@ text...
NOTE: The ":append" and ":insert" commands don't work NOTE: The ":append" and ":insert" commands don't work
properly inside a ":while" and ":for" loop. 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* :endfo[r] *:endfo* *:endfor*
Repeat the commands between ":for" and ":endfor" for Repeat the commands between ":for" and ":endfor" for
each item in {list}. Variable {var} is set to the each item in {object}. {object} can be a |List| or
value of each item. a |Blob|. Variable {var} is set to the value of each
When an error is detected for a command inside the item. When an error is detected for a command inside
loop, execution continues after the "endfor". the loop, execution continues after the "endfor".
Changing {list} inside the loop affects what items are Changing {object} inside the loop affects what items
used. Make a copy if this is unwanted: > are used. Make a copy if this is unwanted: >
:for item in copy(mylist) :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 When {object} is a |List| and not making a copy, Vim
with the current item. Thus the current item can be stores a reference to the next item in the |List|
removed without effect. Removing any later item means before executing the commands with the current item.
it will not be found. Thus the following example Thus the current item can be removed without effect.
works (an inefficient way to make a list empty): > 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 for item in mylist
call remove(mylist, 0) call remove(mylist, 0)
endfor 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. 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} :for [{var1}, {var2}, ...] in {listlist}
:endfo[r] :endfo[r]
Like ":for" above, but each item in {listlist} must be 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); tv_clear(&tv);
} else { } else {
EMSG(_(e_listreq)); EMSG(_(e_listblobreq));
tv_clear(&tv); tv_clear(&tv);
} }
} }

View File

@ -4984,12 +4984,16 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool error = false; bool error = false;
if (argvars[0].v_type == VAR_BLOB) { 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; return;
} }
long before = 0; 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) { if (argvars[2].v_type != VAR_UNKNOWN) {
before = (long)tv_get_number_chk(&argvars[2], &error); 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; return;
} }
ga_grow(&argvars[0].vval.v_blob->bv_ga, 1); ga_grow(&b->bv_ga, 1);
char_u *const p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data; char_u *const p = (char_u *)b->bv_ga.ga_data;
memmove(p + before + 1, p + before, (size_t)len - before); memmove(p + before + 1, p + before, (size_t)len - before);
*(p + before) = val; *(p + before) = val;
argvars[0].vval.v_blob->bv_ga.ga_len++; b->bv_ga.ga_len++;
tv_copy(&argvars[0], rettv); tv_copy(&argvars[0], rettv);
} else if (argvars[0].v_type != VAR_LIST) { } 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) { } 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; bool error = false;
idx = (long)tv_get_number_chk(&argvars[1], &error); idx = (long)tv_get_number_chk(&argvars[1], &error);
if (!error) { if (!error) {
blob_T *const b = argvars[0].vval.v_blob;
const int len = tv_blob_len(b); const int len = tv_blob_len(b);
if (idx < 0) { 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(1, 0)", 'E896:')
call assert_fails("call remove(b, b)", 'E974:') call assert_fails("call remove(b, b)", 'E974:')
call assert_fails("call remove(v:_null_blob, 1, 2)", 'E979:') 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 endfunc
func Test_blob_read_write() 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, 257)', 'E475:')
call assert_fails('call insert(b, 0, [9])', 'E745:') call assert_fails('call insert(b, 0, [9])', 'E745:')
call assert_equal(0, insert(v:_null_blob, 0x33)) 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 endfunc
func Test_blob_reverse() func Test_blob_reverse()

View File

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