Compare commits

...

10 Commits

Author SHA1 Message Date
Bram Moolenaar
7007e31bde patch 8.2.2662: there is no way to avoid some escape sequences
Problem:    There is no way to avoid some escape sequences.
Solution:   Suppress escape sequences when the --not-a-term argument is used.
            (Gary Johnson)
2021-03-27 12:11:33 +01:00
Bram Moolenaar
bb5d87c850 patch 8.2.2661: leaking memory when looping over a string
Problem:    Leaking memory when looping over a string.
Solution:   Free the memory.
2021-03-26 22:15:26 +01:00
Bram Moolenaar
ccc25aa285 patch 8.2.2660: Vim9: no error for declaration with trailing text
Problem:    Vim9: no error for declaration with trailing text.
Solution:   Give an error. (closes #8014)
2021-03-26 21:27:52 +01:00
Bram Moolenaar
c61cb8bfe1 patch 8.2.2659: eval test fails because for loop on string works
Problem:    Eval test fails because for loop on string works.
Solution:   Check looping over function reference fails.
2021-03-26 20:56:45 +01:00
Bram Moolenaar
74e54fcb44 patch 8.2.2658: :for cannot loop over a string
Problem:    :for cannot loop over a string.
Solution:   Accept a string argument and iterate over its characters.
2021-03-26 20:41:29 +01:00
Bram Moolenaar
522eefd9a2 patch 8.2.2657: Vim9: error message for declaring variable in for loop
Problem:    Vim9: error message for declaring variable in for loop.
Solution:   Clear variables when entering block again. (closes #8012)
2021-03-26 18:49:22 +01:00
Bram Moolenaar
a2b3e7dc92 patch 8.2.2656: some command line arguments and regexp errors not tested
Problem:    Some command line arguments and regexp errors not tested.
Solution:   Add a few test cases. (Dominique Pellé, closes #8013)
2021-03-26 17:24:34 +01:00
Bram Moolenaar
0a1a6a1aa4 patch 8.2.2655: The -w command line argument doesn't work
Problem:    The -w command line argument doesn't work.
Solution:   Don't set 'window' when set with the -w argument. (closes #8011)
2021-03-26 14:14:18 +01:00
Bram Moolenaar
ff87140046 patch 8.2.2654: Vim9: getting a character from a string can be slow
Problem:    Vim9: getting a character from a string can be slow.
Solution:   Avoid a function call to get the character byte size. (#8000)
2021-03-26 13:34:05 +01:00
Bram Moolenaar
3a0f092ac0 patch 8.2.2653: build failure
Problem:    Build failure.
Solution:   Add missing changes.
2021-03-25 22:22:30 +01:00
18 changed files with 336 additions and 65 deletions

View File

@@ -439,8 +439,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, String or Blob.
A variable is set to each item in sequence. Example with a List: >
:for item in mylist
: call Doit(item)
:endfor
@@ -457,7 +457,7 @@ If all you want to do is modify each item in the list then the |map()|
function will be a simpler method than a for loop.
Just like the |:let| command, |:for| also accepts a list of variables. This
requires the argument to be a list of lists. >
requires the argument to be a List of Lists. >
:for [lnum, col] in [[1, 3], [2, 8], [3, 0]]
: call Doit(lnum, col)
:endfor
@@ -473,6 +473,14 @@ It is also possible to put remaining items in a List variable: >
: endif
:endfor
For a Blob one byte at a time is used.
For a String one character, including any composing characters, is used as a
String. Example: >
for c in text
echo 'This character is ' .. c
endfor
List functions ~
*E714*

View File

@@ -386,4 +386,8 @@ EXTERN char e_text_found_after_enddef_str[]
EXTERN char e_string_required_for_argument_nr[]
INIT(= N_("E1174: String required for argument %d"));
EXTERN char e_non_empty_string_required_for_argument_nr[]
INIT(= N_("E1142: Non-empty string required for argument %d"));
INIT(= N_("E1175: Non-empty string required for argument %d"));
EXTERN char e_misplaced_command_modifier[]
INIT(= N_("E1176: Misplaced command modifier"));
EXTERN char e_for_loop_on_str_not_supported[]
INIT(= N_("E1177: For loop on %s not supported"));

View File

@@ -41,6 +41,8 @@ typedef struct
list_T *fi_list; // list being used
int fi_bi; // index of blob
blob_T *fi_blob; // blob being used
char_u *fi_string; // copy of string being used
int fi_byte_idx; // byte index in fi_string
} forinfo_T;
static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op);
@@ -1738,6 +1740,14 @@ eval_for_line(
}
clear_tv(&tv);
}
else if (tv.v_type == VAR_STRING)
{
fi->fi_byte_idx = 0;
fi->fi_string = tv.vval.v_string;
tv.vval.v_string = NULL;
if (fi->fi_string == NULL)
fi->fi_string = vim_strsave((char_u *)"");
}
else
{
emsg(_(e_listreq));
@@ -1790,7 +1800,25 @@ next_for_item(void *fi_void, char_u *arg)
tv.vval.v_number = blob_get(fi->fi_blob, fi->fi_bi);
++fi->fi_bi;
return ex_let_vars(arg, &tv, TRUE, fi->fi_semicolon,
fi->fi_varcount, flag, NULL) == OK;
fi->fi_varcount, flag, NULL) == OK;
}
if (fi->fi_string != NULL)
{
typval_T tv;
int len;
len = mb_ptr2len(fi->fi_string + fi->fi_byte_idx);
if (len == 0)
return FALSE;
tv.v_type = VAR_STRING;
tv.v_lock = VAR_FIXED;
tv.vval.v_string = vim_strnsave(fi->fi_string + fi->fi_byte_idx, len);
fi->fi_byte_idx += len;
result = ex_let_vars(arg, &tv, TRUE, fi->fi_semicolon,
fi->fi_varcount, flag, NULL) == OK;
vim_free(tv.vval.v_string);
return result;
}
item = fi->fi_lw.lw_item;
@@ -1800,7 +1828,7 @@ next_for_item(void *fi_void, char_u *arg)
{
fi->fi_lw.lw_item = item->li_next;
result = (ex_let_vars(arg, &item->li_tv, TRUE, fi->fi_semicolon,
fi->fi_varcount, flag, NULL) == OK);
fi->fi_varcount, flag, NULL) == OK);
}
return result;
}
@@ -1813,13 +1841,17 @@ free_for_info(void *fi_void)
{
forinfo_T *fi = (forinfo_T *)fi_void;
if (fi != NULL && fi->fi_list != NULL)
if (fi == NULL)
return;
if (fi->fi_list != NULL)
{
list_rem_watch(fi->fi_list, &fi->fi_lw);
list_unref(fi->fi_list);
}
if (fi != NULL && fi->fi_blob != NULL)
else if (fi->fi_blob != NULL)
blob_unref(fi->fi_blob);
else
vim_free(fi->fi_string);
vim_free(fi);
}

View File

@@ -789,8 +789,11 @@ ex_let(exarg_T *eap)
{
if (vim9script)
{
// Vim9 declaration ":var name: type"
arg = vim9_declare_scriptvar(eap, arg);
if (!ends_excmd2(eap->cmd, skipwhite(argend)))
semsg(_(e_trailing_arg), argend);
else
// Vim9 declaration ":var name: type"
arg = vim9_declare_scriptvar(eap, arg);
}
else
{

View File

@@ -1154,6 +1154,32 @@ ex_while(exarg_T *eap)
++cstack->cs_looplevel;
cstack->cs_line[cstack->cs_idx] = -1;
}
else
{
if (in_vim9script() && SCRIPT_ID_VALID(current_sctx.sc_sid))
{
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
int i;
// Any variables defined in the previous round are no longer
// visible.
for (i = cstack->cs_script_var_len[cstack->cs_idx];
i < si->sn_var_vals.ga_len; ++i)
{
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + i;
// sv_name is set to NULL if it was already removed. This
// happens when it was defined in an inner block and no
// functions were defined there.
if (sv->sv_name != NULL)
// Remove a variable declared inside the block, if it
// still exists, from sn_vars.
hide_script_var(si, i, FALSE);
}
cstack->cs_script_var_len[cstack->cs_idx] =
si->sn_var_vals.ga_len;
}
}
cstack->cs_flags[cstack->cs_idx] =
eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR;
@@ -1175,6 +1201,9 @@ ex_while(exarg_T *eap)
void *fi;
evalarg_T evalarg;
/*
* ":for var in list-expr"
*/
CLEAR_FIELD(evalarg);
evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE;
if (getline_equal(eap->getline, eap->cookie, getsourceline))
@@ -1183,9 +1212,6 @@ ex_while(exarg_T *eap)
evalarg.eval_cookie = eap->cookie;
}
/*
* ":for var in list-expr"
*/
if ((cstack->cs_lflags & CSL_HAD_LOOP) != 0)
{
// Jumping here from a ":continue" or ":endfor": use the
@@ -1384,10 +1410,8 @@ ex_endwhile(exarg_T *eap)
&& dbg_check_skipped(eap))
(void)do_intthrow(cstack);
/*
* Set loop flag, so do_cmdline() will jump back to the matching
* ":while" or ":for".
*/
// Set loop flag, so do_cmdline() will jump back to the matching
// ":while" or ":for".
cstack->cs_lflags |= CSL_HAD_ENDLOOP;
}
}

View File

@@ -995,6 +995,19 @@ is_not_a_term()
return params.not_a_term;
}
/*
* Return TRUE when the --not-a-term argument was found or the GUI is in use.
*/
static int
is_not_a_term_or_gui()
{
return params.not_a_term
#ifdef FEAT_GUI
|| gui.in_use
#endif
;
}
// When TRUE in a safe state when starting to wait for a character.
static int was_safe = FALSE;
@@ -1528,9 +1541,7 @@ getout(int exitval)
#endif
// Position the cursor on the last screen line, below all the text
#ifdef FEAT_GUI
if (!gui.in_use)
#endif
if (!is_not_a_term_or_gui())
windgoto((int)Rows - 1, 0);
#if defined(FEAT_EVAL) || defined(FEAT_SYN_HL)
@@ -1640,9 +1651,7 @@ getout(int exitval)
}
// Position the cursor again, the autocommands may have moved it
#ifdef FEAT_GUI
if (!gui.in_use)
#endif
if (!is_not_a_term_or_gui())
windgoto((int)Rows - 1, 0);
#ifdef FEAT_JOB_CHANNEL

View File

@@ -3343,7 +3343,7 @@ exit_scroll(void)
else
out_char('\n');
}
else
else if (!is_not_a_term())
{
restore_cterm_colors(); // get original colors back
msg_clr_eos_force(); // clear the rest of the display
@@ -3370,9 +3370,12 @@ mch_exit(int r)
{
settmode(TMODE_COOK);
#ifdef FEAT_TITLE
// restore xterm title and icon name
mch_restore_title(SAVE_RESTORE_BOTH);
term_pop_title(SAVE_RESTORE_BOTH);
if (!is_not_a_term())
{
// restore xterm title and icon name
mch_restore_title(SAVE_RESTORE_BOTH);
term_pop_title(SAVE_RESTORE_BOTH);
}
#endif
/*
* When t_ti is not empty but it doesn't cause swapping terminal

View File

@@ -3365,8 +3365,9 @@ win_new_shellsize(void)
ui_new_shellsize();
if (old_Rows != Rows)
{
// if 'window' uses the whole screen, keep it using that
if (p_window == old_Rows - 1 || old_Rows == 0)
// If 'window' uses the whole screen, keep it using that.
// Don't change it when set with "-w size" on the command line.
if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0))
p_window = Rows - 1;
old_Rows = Rows;
shell_new_rows(); // update window sizes

View File

@@ -66,7 +66,7 @@ endfunc
func Test_for_invalid()
call assert_fails("for x in 99", 'E714:')
call assert_fails("for x in 'asdf'", 'E714:')
call assert_fails("for x in function('winnr')", 'E714:')
call assert_fails("for x in {'a': 9}", 'E714:')
if 0

View File

@@ -924,8 +924,17 @@ func Test_regexp_error()
call assert_fails("call matchlist('x x', '\\%#=1 \\ze*')", 'E888:')
call assert_fails("call matchlist('x x', '\\%#=2 \\zs*')", 'E888:')
call assert_fails("call matchlist('x x', '\\%#=2 \\ze*')", 'E888:')
call assert_fails('exe "normal /\\%#=1\\%[x\\%[x]]\<CR>"', 'E369:')
call assert_fails("call matchstr('abcd', '\\%o841\\%o142')", 'E678:')
call assert_fails("call matchstr('abcd', '\\%#=2\\%2147483647c')", 'E951:')
call assert_fails("call matchstr('abcd', '\\%#=2\\%2147483647l')", 'E951:')
call assert_fails("call matchstr('abcd', '\\%#=2\\%2147483647v')", 'E951:')
call assert_fails('exe "normal /\\%#=1\\%[x\\%[x]]\<CR>"', 'E369:')
call assert_fails('exe "normal /\\%#=2\\%2147483647l\<CR>"', 'E951:')
call assert_fails('exe "normal /\\%#=2\\%2147483647c\<CR>"', 'E951:')
call assert_fails('exe "normal /\\%#=2\\%102261126v\<CR>"', 'E951:')
call assert_fails('exe "normal /\\%#=2\\%2147483646l\<CR>"', 'E486:')
call assert_fails('exe "normal /\\%#=2\\%2147483646c\<CR>"', 'E486:')
call assert_fails('exe "normal /\\%#=2\\%102261125v\<CR>"', 'E486:')
call assert_equal('', matchstr('abcd', '\%o181\%o142'))
endfunc

View File

@@ -860,10 +860,12 @@ func Test_t_arg()
\ 'Xtags')
call writefile([' first', ' second', ' third'], 'Xfile1')
if RunVim(before, after, '-t second')
call assert_equal(['Xfile1:L2C5'], readfile('Xtestout'))
call delete('Xtestout')
endif
for t_arg in ['-t second', '-tsecond']
if RunVim(before, after, '-t second')
call assert_equal(['Xfile1:L2C5'], readfile('Xtestout'), t_arg)
call delete('Xtestout')
endif
endfor
call delete('Xtags')
call delete('Xfile1')
@@ -1041,10 +1043,37 @@ func Test_io_not_a_terminal()
\ 'Vim: Warning: Input is not from a terminal'], l)
endfunc
" Test for --not-a-term avoiding escape codes.
func Test_not_a_term()
CheckUnix
CheckNotGui
if &shellredir =~ '%s'
let redir = printf(&shellredir, 'Xvimout')
else
let redir = &shellredir .. ' Xvimout'
endif
" Without --not-a-term there are a few escape sequences.
" This will take 2 seconds because of the missing --not-a-term
let cmd = GetVimProg() .. ' --cmd quit ' .. redir
exe "silent !" . cmd
call assert_match("\<Esc>", readfile('Xvimout')->join())
call delete('Xvimout')
" With --not-a-term there are no escape sequences.
let cmd = GetVimProg() .. ' --not-a-term --cmd quit ' .. redir
exe "silent !" . cmd
call assert_notmatch("\<Esc>", readfile('Xvimout')->join())
call delete('Xvimout')
endfunc
" Test for the "-w scriptout" argument
func Test_w_arg()
" Can't catch the output of gvim.
CheckNotGui
call writefile(["iVim Editor\<Esc>:q!\<CR>"], 'Xscriptin', 'b')
if RunVim([], [], '-s Xscriptin -w Xscriptout')
call assert_equal(["iVim Editor\e:q!\r"], readfile('Xscriptout'))
@@ -1060,6 +1089,16 @@ func Test_w_arg()
call assert_equal("Cannot open for script output: \"Xdir\"\n", m)
call delete("Xdir", 'rf')
endif
" A number argument sets the 'window' option
call writefile(["iwindow \<C-R>=&window\<CR>\<Esc>:wq! Xresult\<CR>"], 'Xscriptin', 'b')
for w_arg in ['-w 17', '-w17']
if RunVim([], [], '-s Xscriptin ' .. w_arg)
call assert_equal(["window 17"], readfile('Xresult'), w_arg)
call delete('Xresult')
endif
endfor
call delete('Xscriptin')
endfunc
" Test for the "-s scriptin" argument

View File

@@ -1290,6 +1290,8 @@ def Test_var_declaration()
other = 1234
g:other_var = other
var xyz: string # comment
# type is inferred
var s:dict = {['a']: 222}
def GetDictVal(key: any)
@@ -1365,7 +1367,7 @@ def Test_var_declaration_fails()
vim9script
var 9var: string
END
CheckScriptFailure(lines, 'E475:')
CheckScriptFailure(lines, 'E488:')
CheckDefFailure(['var foo.bar = 2'], 'E1087:')
CheckDefFailure(['var foo[3] = 2'], 'E1087:')
@@ -1617,6 +1619,11 @@ def Test_expr_error_no_assign()
echo x
END
CheckScriptFailureList(lines, ['E1154:', 'E121:'])
lines =<< trim END
var x: string 'string'
END
CheckDefAndScriptFailure(lines, 'E488:')
enddef

View File

@@ -1061,7 +1061,6 @@ def Test_disassemble_for_loop_eval()
'\d STORE -1 in $1\_s*' ..
'\d PUSHS "\["one", "two"\]"\_s*' ..
'\d BCALL eval(argc 1)\_s*' ..
'\d CHECKTYPE list<any> stack\[-1\]\_s*' ..
'\d FOR $1 -> \d\+\_s*' ..
'\d STORE $2\_s*' ..
'res ..= str\_s*' ..
@@ -1071,7 +1070,7 @@ def Test_disassemble_for_loop_eval()
'\d\+ CONCAT\_s*' ..
'\d\+ STORE $0\_s*' ..
'endfor\_s*' ..
'\d\+ JUMP -> 6\_s*' ..
'\d\+ JUMP -> 5\_s*' ..
'\d\+ DROP\_s*' ..
'return res\_s*' ..
'\d\+ LOAD $0\_s*' ..

View File

@@ -2263,6 +2263,13 @@ def Test_for_outside_of_function()
endfor
assert_equal(['', '0', '1', '2', '3'], getline(1, '$'))
bwipe!
var result = ''
for i in [1, 2, 3]
var loop = ' loop ' .. i
result ..= loop
endfor
assert_equal(' loop 1 loop 2 loop 3', result)
END
writefile(lines, 'Xvim9for.vim')
source Xvim9for.vim
@@ -2315,6 +2322,25 @@ def Test_for_loop()
res ..= n .. s
endfor
assert_equal('1a2b', res)
# loop over string
res = ''
for c in 'aéc̀d'
res ..= c .. '-'
endfor
assert_equal('a-é-c̀-d-', res)
res = ''
for c in ''
res ..= c .. '-'
endfor
assert_equal('', res)
res = ''
for c in test_null_string()
res ..= c .. '-'
endfor
assert_equal('', res)
enddef
def Test_for_loop_fails()
@@ -2326,10 +2352,17 @@ def Test_for_loop_fails()
CheckDefFailure(['var x = 5', 'for x in range(5)'], 'E1017:')
CheckScriptFailure(['def Func(arg: any)', 'for arg in range(5)', 'enddef', 'defcompile'], 'E1006:')
delfunc! g:Func
CheckDefFailure(['for i in "text"'], 'E1012:')
CheckDefFailure(['for i in xxx'], 'E1001:')
CheckDefFailure(['endfor'], 'E588:')
CheckDefFailure(['for i in range(3)', 'echo 3'], 'E170:')
# wrong type detected at compile time
CheckDefFailure(['for i in {a: 1}', 'echo 3', 'endfor'], 'E1177: For loop on dict not supported')
# wrong type detected at runtime
g:adict = {a: 1}
CheckDefExecFailure(['for i in g:adict', 'echo 3', 'endfor'], 'E1177: For loop on dict not supported')
unlet g:adict
enddef
def Test_for_loop_script_var()

View File

@@ -7484,6 +7484,26 @@ func Test_trinary_expression()
call assert_equal(v:false, eval(string(v:false)))
endfunction
func Test_for_over_string()
let res = ''
for c in 'aéc̀d'
let res ..= c .. '-'
endfor
call assert_equal('a-é-c̀-d-', res)
let res = ''
for c in ''
let res ..= c .. '-'
endfor
call assert_equal('', res)
let res = ''
for c in test_null_string()
let res ..= c .. '-'
endfor
call assert_equal('', res)
endfunc
"-------------------------------------------------------------------------------
" Modelines {{{1
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@@ -750,6 +750,26 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2662,
/**/
2661,
/**/
2660,
/**/
2659,
/**/
2658,
/**/
2657,
/**/
2656,
/**/
2655,
/**/
2654,
/**/
2653,
/**/
2652,
/**/

View File

@@ -7264,11 +7264,15 @@ compile_for(char_u *arg_start, cctx_T *cctx)
}
arg_end = arg;
// Now that we know the type of "var", check that it is a list, now or at
// runtime.
// If we know the type of "var" and it is a not a list or string we can
// give an error now.
vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
if (need_type(vartype, &t_list_any, -1, 0, cctx, FALSE, FALSE) == FAIL)
if (vartype->tt_type != VAR_LIST && vartype->tt_type != VAR_STRING
&& vartype->tt_type != VAR_ANY)
{
// TODO: support Blob
semsg(_(e_for_loop_on_str_not_supported),
vartype_name(vartype->tt_type));
drop_scope(cctx);
return NULL;
}

View File

@@ -1067,13 +1067,22 @@ char_from_string(char_u *str, varnumber_T index)
return NULL;
slen = STRLEN(str);
// do the same as for a list: a negative index counts from the end
// Do the same as for a list: a negative index counts from the end.
// Optimization to check the first byte to be below 0x80 (and no composing
// character follows) makes this a lot faster.
if (index < 0)
{
int clen = 0;
for (nbyte = 0; nbyte < slen; ++clen)
nbyte += mb_ptr2len(str + nbyte);
{
if (str[nbyte] < 0x80 && str[nbyte + 1] < 0x80)
++nbyte;
else if (enc_utf8)
nbyte += utfc_ptr2len(str + nbyte);
else
nbyte += mb_ptr2len(str + nbyte);
}
nchar = clen + index;
if (nchar < 0)
// unlike list: index out of range results in empty string
@@ -1081,7 +1090,14 @@ char_from_string(char_u *str, varnumber_T index)
}
for (nbyte = 0; nchar > 0 && nbyte < slen; --nchar)
nbyte += mb_ptr2len(str + nbyte);
{
if (str[nbyte] < 0x80 && str[nbyte + 1] < 0x80)
++nbyte;
else if (enc_utf8)
nbyte += utfc_ptr2len(str + nbyte);
else
nbyte += mb_ptr2len(str + nbyte);
}
if (nbyte >= slen)
return NULL;
return vim_strnsave(str + nbyte, mb_ptr2len(str + nbyte));
@@ -2725,36 +2741,76 @@ call_def_function(
// top of a for loop
case ISN_FOR:
{
list_T *list = STACK_TV_BOT(-1)->vval.v_list;
typval_T *ltv = STACK_TV_BOT(-1);
typval_T *idxtv =
STACK_TV_VAR(iptr->isn_arg.forloop.for_idx);
// push the next item from the list
if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
goto failed;
++idxtv->vval.v_number;
if (list == NULL || idxtv->vval.v_number >= list->lv_len)
if (ltv->v_type == VAR_LIST)
{
// past the end of the list, jump to "endfor"
ectx.ec_iidx = iptr->isn_arg.forloop.for_end;
may_restore_cmdmod(&funclocal);
}
else if (list->lv_first == &range_list_item)
{
// non-materialized range() list
tv = STACK_TV_BOT(0);
tv->v_type = VAR_NUMBER;
tv->v_lock = 0;
tv->vval.v_number = list_find_nr(
list_T *list = ltv->vval.v_list;
// push the next item from the list
++idxtv->vval.v_number;
if (list == NULL
|| idxtv->vval.v_number >= list->lv_len)
{
// past the end of the list, jump to "endfor"
ectx.ec_iidx = iptr->isn_arg.forloop.for_end;
may_restore_cmdmod(&funclocal);
}
else if (list->lv_first == &range_list_item)
{
// non-materialized range() list
tv = STACK_TV_BOT(0);
tv->v_type = VAR_NUMBER;
tv->v_lock = 0;
tv->vval.v_number = list_find_nr(
list, idxtv->vval.v_number, NULL);
++ectx.ec_stack.ga_len;
++ectx.ec_stack.ga_len;
}
else
{
listitem_T *li = list_find(list,
idxtv->vval.v_number);
copy_tv(&li->li_tv, STACK_TV_BOT(0));
++ectx.ec_stack.ga_len;
}
}
else if (ltv->v_type == VAR_STRING)
{
char_u *str = ltv->vval.v_string;
int len = str == NULL ? 0 : (int)STRLEN(str);
// Push the next character from the string. The index
// is for the last byte of the previous character.
++idxtv->vval.v_number;
if (idxtv->vval.v_number >= len)
{
// past the end of the string, jump to "endfor"
ectx.ec_iidx = iptr->isn_arg.forloop.for_end;
may_restore_cmdmod(&funclocal);
}
else
{
int clen = mb_ptr2len(str + idxtv->vval.v_number);
tv = STACK_TV_BOT(0);
tv->v_type = VAR_STRING;
tv->vval.v_string = vim_strnsave(
str + idxtv->vval.v_number, clen);
++ectx.ec_stack.ga_len;
idxtv->vval.v_number += clen - 1;
}
}
else
{
listitem_T *li = list_find(list, idxtv->vval.v_number);
copy_tv(&li->li_tv, STACK_TV_BOT(0));
++ectx.ec_stack.ga_len;
// TODO: support Blob
semsg(_(e_for_loop_on_str_not_supported),
vartype_name(ltv->v_type));
goto failed;
}
}
break;