# HG changeset patch # User Bram Moolenaar # Date 1618745403 -7200 # Node ID 943e9b1d2d169c907bbee63f941fdf38f5442931 # Parent 22651692d78e434053e300d0794e35dd96edd911 patch 8.2.2780: Vim9: for loop over blob doesn't work Commit: https://github.com/vim/vim/commit/d551d6c268e435e2fbba22775510fbd0a54477f6 Author: Bram Moolenaar Date: Sun Apr 18 13:15:58 2021 +0200 patch 8.2.2780: Vim9: for loop over blob doesn't work Problem: Vim9: for loop over blob doesn't work. Solution: Make it work. diff --git a/src/testdir/test_blob.vim b/src/testdir/test_blob.vim --- a/src/testdir/test_blob.vim +++ b/src/testdir/test_blob.vim @@ -283,33 +283,36 @@ func Test_blob_index_assign() endfunc func Test_blob_for_loop() - let blob = 0z00010203 - let i = 0 - for byte in blob - call assert_equal(i, byte) - let i += 1 - endfor - call assert_equal(4, i) + let lines =<< trim END + VAR blob = 0z00010203 + VAR i = 0 + for byte in blob + call assert_equal(i, byte) + LET i += 1 + endfor + call assert_equal(4, i) - let blob = 0z00 - call remove(blob, 0) - call assert_equal(0, len(blob)) - for byte in blob - call assert_error('loop over empty blob') - endfor + LET blob = 0z00 + call remove(blob, 0) + call assert_equal(0, len(blob)) + for byte in blob + call assert_report('loop over empty blob') + endfor - let blob = 0z0001020304 - let i = 0 - for byte in blob - call assert_equal(i, byte) - if i == 1 - call remove(blob, 0) - elseif i == 3 - call remove(blob, 3) - endif - let i += 1 - endfor - call assert_equal(5, i) + LET blob = 0z0001020304 + LET i = 0 + for byte in blob + call assert_equal(i, byte) + if i == 1 + call remove(blob, 0) + elseif i == 3 + call remove(blob, 3) + endif + LET i += 1 + endfor + call assert_equal(5, i) + END + call CheckLegacyAndVim9Success(lines) endfunc func Test_blob_concatenate() diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2780, +/**/ 2779, /**/ 2778, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -7508,13 +7508,12 @@ compile_for(char_u *arg_start, cctx_T *c } arg_end = arg; - // If we know the type of "var" and it is a not a list or string we can + // If we know the type of "var" and it is a not a supported type we can // give an error now. vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1]; if (vartype->tt_type != VAR_LIST && vartype->tt_type != VAR_STRING - && vartype->tt_type != VAR_ANY) - { - // TODO: support Blob + && vartype->tt_type != VAR_BLOB && vartype->tt_type != VAR_ANY) + { semsg(_(e_for_loop_on_str_not_supported), vartype_name(vartype->tt_type)); drop_scope(cctx); @@ -7523,6 +7522,8 @@ compile_for(char_u *arg_start, cctx_T *c if (vartype->tt_type == VAR_STRING) item_type = &t_string; + else if (vartype->tt_type == VAR_BLOB) + item_type = &t_number; else if (vartype->tt_type == VAR_LIST && vartype->tt_member->tt_type != VAR_ANY) { @@ -7530,7 +7531,7 @@ compile_for(char_u *arg_start, cctx_T *c item_type = vartype->tt_member; else if (vartype->tt_member->tt_type == VAR_LIST && vartype->tt_member->tt_member->tt_type != VAR_ANY) - // TODO: should get the type from + // TODO: should get the type for each lhs item_type = vartype->tt_member->tt_member; } diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2900,8 +2900,8 @@ call_def_function( { char_u *str = ltv->vval.v_string; - // Push the next character from the string. The index - // is for the last byte of the previous character. + // The index is for the last byte of the previous + // character. ++idxtv->vval.v_number; if (str == NULL || str[idxtv->vval.v_number] == NUL) { @@ -2913,6 +2913,7 @@ call_def_function( { int clen = mb_ptr2len(str + idxtv->vval.v_number); + // Push the next character from the string. tv = STACK_TV_BOT(0); tv->v_type = VAR_STRING; tv->vval.v_string = vim_strnsave( @@ -2921,9 +2922,41 @@ call_def_function( idxtv->vval.v_number += clen - 1; } } + else if (ltv->v_type == VAR_BLOB) + { + blob_T *blob = ltv->vval.v_blob; + + // When we get here the first time make a copy of the + // blob, so that the iteration still works when it is + // changed. + if (idxtv->vval.v_number == -1 && blob != NULL) + { + blob_copy(blob, ltv); + blob_unref(blob); + blob = ltv->vval.v_blob; + } + + // The index is for the previous byte. + ++idxtv->vval.v_number; + if (blob == NULL + || idxtv->vval.v_number >= blob_len(blob)) + { + // past the end of the blob, jump to "endfor" + ectx.ec_iidx = iptr->isn_arg.forloop.for_end; + may_restore_cmdmod(&funclocal); + } + else + { + // Push the next byte from the blob. + tv = STACK_TV_BOT(0); + tv->v_type = VAR_NUMBER; + tv->vval.v_number = blob_get(blob, + idxtv->vval.v_number); + ++ectx.ec_stack.ga_len; + } + } else { - // TODO: support Blob semsg(_(e_for_loop_on_str_not_supported), vartype_name(ltv->v_type)); goto failed;