# HG changeset patch # User Bram Moolenaar # Date 1593362704 -7200 # Node ID 111f877e63d96a2d6e7efeaa7f7555506a99ac9a # Parent 77d9be8bbf27b128c07fac9b9e1b72a659a77e67 patch 8.2.1080: Vim9: no line break allowed in a for loop Commit: https://github.com/vim/vim/commit/b7a78f7a6713f07d2fcad0b27dea22925c7b1cdf Author: Bram Moolenaar Date: Sun Jun 28 18:43:40 2020 +0200 patch 8.2.1080: Vim9: no line break allowed in a for loop Problem: Vim9: no line break allowed in a for loop. Solution: Skip line breaks in for command. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -38,6 +38,7 @@ typedef struct { int fi_semicolon; // TRUE if ending in '; var]' int fi_varcount; // nr of variables in the list + int fi_break_count; // nr of line breaks encountered listwatch_T fi_lw; // keep an eye on the item used. list_T *fi_list; // list being used int fi_bi; // index of blob @@ -344,6 +345,7 @@ eval_to_string_skip( } if (skip) --emsg_skip; + clear_evalarg(&EVALARG_EVALUATE, eap); return retval; } @@ -461,6 +463,7 @@ eval_to_string( retval = vim_strsave(tv_get_string(&tv)); clear_tv(&tv); } + clear_evalarg(&EVALARG_EVALUATE, NULL); return retval; } @@ -528,6 +531,7 @@ eval_expr(char_u *arg, exarg_T *eap) tv = ALLOC_ONE(typval_T); if (tv != NULL && eval0(arg, tv, eap, &EVALARG_EVALUATE) == FAIL) VIM_CLEAR(tv); + clear_evalarg(&EVALARG_EVALUATE, eap); return tv; } @@ -675,6 +679,7 @@ eval_foldexpr(char_u *arg, int *cp) if (use_sandbox) --sandbox; --textwinlock; + clear_evalarg(&EVALARG_EVALUATE, NULL); return (int)retval; } @@ -1481,16 +1486,14 @@ eval_for_line( char_u *arg, int *errp, exarg_T *eap, - int skip) + evalarg_T *evalarg) { forinfo_T *fi; char_u *expr; typval_T tv; list_T *l; - evalarg_T evalarg; - - CLEAR_FIELD(evalarg); - evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE; + int skip = !(evalarg->eval_flags & EVAL_EVALUATE); + *errp = TRUE; // default: there is an error fi = ALLOC_CLEAR_ONE(forinfo_T); @@ -1501,8 +1504,9 @@ eval_for_line( if (expr == NULL) return fi; - expr = skipwhite(expr); - if (expr[0] != 'i' || expr[1] != 'n' || !VIM_ISWHITE(expr[2])) + expr = skipwhite_and_linebreak(expr, evalarg); + if (expr[0] != 'i' || expr[1] != 'n' + || !(expr[2] == NUL || VIM_ISWHITE(expr[2]))) { emsg(_(e_missing_in)); return fi; @@ -1510,7 +1514,8 @@ eval_for_line( if (skip) ++emsg_skip; - if (eval0(skipwhite(expr + 2), &tv, eap, &evalarg) == OK) + expr = skipwhite_and_linebreak(expr + 2, evalarg); + if (eval0(expr, &tv, eap, evalarg) == OK) { *errp = FALSE; if (!skip) @@ -1558,11 +1563,25 @@ eval_for_line( } if (skip) --emsg_skip; + fi->fi_break_count = evalarg->eval_break_count; return fi; } /* + * Used when looping over a :for line, skip the "in expr" part. + */ + void +skip_for_lines(void *fi_void, evalarg_T *evalarg) +{ + forinfo_T *fi = (forinfo_T *)fi_void; + int i; + + for (i = 0; i < fi->fi_break_count; ++i) + eval_next_line(evalarg); +} + +/* * Use the first item in a ":for" list. Advance to the next. * Assign the values to the variable (list). "arg" points to the first one. * Return TRUE when a valid item was found, FALSE when at end of list or @@ -1866,6 +1885,7 @@ eval_next_line(evalarg_T *evalarg) char_u *line; line = evalarg->eval_getline(0, evalarg->eval_cookie, 0, TRUE); + ++evalarg->eval_break_count; if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK) { // Going to concatenate the lines after parsing. @@ -1898,14 +1918,19 @@ skipwhite_and_linebreak(char_u *arg, eva void clear_evalarg(evalarg_T *evalarg, exarg_T *eap) { - if (evalarg != NULL && eap != NULL && evalarg->eval_tofree != NULL) + if (evalarg != NULL && evalarg->eval_tofree != NULL) { - // We may need to keep the original command line, e.g. for - // ":let" it has the variable names. But we may also need the - // new one, "nextcmd" points into it. Keep both. - vim_free(eap->cmdline_tofree); - eap->cmdline_tofree = *eap->cmdlinep; - *eap->cmdlinep = evalarg->eval_tofree; + if (eap != NULL) + { + // We may need to keep the original command line, e.g. for + // ":let" it has the variable names. But we may also need the + // new one, "nextcmd" points into it. Keep both. + vim_free(eap->cmdline_tofree); + eap->cmdline_tofree = *eap->cmdlinep; + *eap->cmdlinep = evalarg->eval_tofree; + } + else + vim_free(evalarg->eval_tofree); evalarg->eval_tofree = NULL; } } @@ -1961,8 +1986,6 @@ eval0( if (eap != NULL) eap->nextcmd = check_nextcmd(p); - clear_evalarg(evalarg, eap); - return ret; } diff --git a/src/ex_eval.c b/src/ex_eval.c --- a/src/ex_eval.c +++ b/src/ex_eval.c @@ -899,10 +899,16 @@ ex_eval(exarg_T *eap) CLEAR_FIELD(evalarg); evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE; - evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL; + if (getline_equal(eap->getline, eap->cookie, getsourceline)) + { + evalarg.eval_getline = eap->getline; + evalarg.eval_cookie = eap->cookie; + } if (eval0(eap->arg, &tv, eap, &evalarg) == OK) clear_tv(&tv); + + clear_evalarg(&evalarg, eap); } /* @@ -1108,7 +1114,16 @@ ex_while(exarg_T *eap) } else { - void *fi; + void *fi; + evalarg_T evalarg; + + CLEAR_FIELD(evalarg); + evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE; + if (getline_equal(eap->getline, eap->cookie, getsourceline)) + { + evalarg.eval_getline = eap->getline; + evalarg.eval_cookie = eap->cookie; + } /* * ":for var in list-expr" @@ -1119,11 +1134,14 @@ ex_while(exarg_T *eap) // previously evaluated list. fi = cstack->cs_forinfo[cstack->cs_idx]; error = FALSE; + + // the "in expr" is not used, skip over it + skip_for_lines(fi, &evalarg); } else { // Evaluate the argument and get the info in a structure. - fi = eval_for_line(eap->arg, &error, eap, skip); + fi = eval_for_line(eap->arg, &error, eap, &evalarg); cstack->cs_forinfo[cstack->cs_idx] = fi; } @@ -1138,6 +1156,7 @@ ex_while(exarg_T *eap) free_for_info(fi); cstack->cs_forinfo[cstack->cs_idx] = NULL; } + clear_evalarg(&evalarg, eap); } /* diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -1885,7 +1885,7 @@ EXTERN listitem_T range_list_item; // Passed to an eval() function to enable evaluation. EXTERN evalarg_T EVALARG_EVALUATE # ifdef DO_INIT - = {EVAL_EVALUATE, NULL, NULL, {0, 0, 0, 0, NULL}, NULL} + = {EVAL_EVALUATE, 0, NULL, NULL, {0, 0, 0, 0, NULL}, NULL} # endif ; #endif diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -22,7 +22,8 @@ int eval_foldexpr(char_u *arg, int *cp); char_u *get_lval(char_u *name, typval_T *rettv, lval_T *lp, int unlet, int skip, int flags, int fne_flags); void clear_lval(lval_T *lp); void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, int flags, char_u *op); -void *eval_for_line(char_u *arg, int *errp, exarg_T *eap, int skip); +void *eval_for_line(char_u *arg, int *errp, exarg_T *eap, evalarg_T *evalarg); +void skip_for_lines(void *fi_void, evalarg_T *evalarg); int next_for_item(void *fi_void, char_u *arg); void free_for_info(void *fi_void); void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1758,11 +1758,12 @@ typedef struct // Struct passed through eval() functions. // See EVALARG_EVALUATE for a fixed value with eval_flags set to EVAL_EVALUATE. typedef struct { - int eval_flags; // EVAL_ flag values below + int eval_flags; // EVAL_ flag values below + int eval_break_count; // nr of line breaks consumed // copied from exarg_T when "getline" is "getsourceline". Can be NULL. char_u *(*eval_getline)(int, void *, int, int); - void *eval_cookie; // argument for eval_getline() + void *eval_cookie; // argument for eval_getline() // Used to collect lines while parsing them, so that they can be // concatenated later. Used when "eval_ga.ga_itemsize" is not zero. diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -160,4 +160,35 @@ def Test_while_linebreak() CheckScriptSuccess(lines) enddef +def Test_for_linebreak() + let lines =<< trim END + vim9script + let nr = 0 + for x + in + [1, 2, 3, 4] + nr = nr + x + endfor + assert_equal(10, nr) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + let nr = 0 + for x + in + [1, 2, + 3, 4 + ] + nr = nr + + + x + endfor + assert_equal(10, nr) + END + CheckScriptSuccess(lines) +enddef + + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3825,6 +3825,7 @@ ex_return(exarg_T *eap) if (eap->skip) --emsg_skip; + clear_evalarg(&evalarg, eap); } /* diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1080, +/**/ 1079, /**/ 1078,