Patch 8.2.1080
Problem: Vim9: no line break allowed in a for loop.
Solution: Skip line breaks in for command.
Files: src/eval.c, src/ex_eval.c, src/proto/
eval.pro, src/userfunc.c,
src/structs.h, src/globals.h, src/testdir/test_vim9_cmd.vim
*** ../vim-8.2.1079/src/eval.c 2020-06-28 15:51:12.145674365 +0200
--- src/eval.c 2020-06-28 18:13:11.781536397 +0200
***************
*** 38,43 ****
--- 38,44 ----
{
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,349 ****
--- 345,351 ----
}
if (skip)
--emsg_skip;
+ clear_evalarg(&EVALARG_EVALUATE, eap);
return retval;
}
***************
*** 461,466 ****
--- 463,469 ----
retval = vim_strsave(tv_get_string(&tv));
clear_tv(&tv);
}
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
return retval;
}
***************
*** 528,533 ****
--- 531,537 ----
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,680 ****
--- 679,685 ----
if (use_sandbox)
--sandbox;
--textwinlock;
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
return (int)retval;
}
***************
*** 1481,1496 ****
char_u *arg,
int *errp,
exarg_T *eap,
! int skip)
{
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;
*errp = TRUE; // default: there is an error
fi = ALLOC_CLEAR_ONE(forinfo_T);
--- 1486,1499 ----
char_u *arg,
int *errp,
exarg_T *eap,
! evalarg_T *evalarg)
{
forinfo_T *fi;
char_u *expr;
typval_T tv;
list_T *l;
! int skip = !(evalarg->eval_flags & EVAL_EVALUATE);
*errp = TRUE; // default: there is an error
fi = ALLOC_CLEAR_ONE(forinfo_T);
***************
*** 1501,1508 ****
if (expr == NULL)
return fi;
! expr = skipwhite(expr);
! if (expr[0] != 'i' || expr[1] != 'n' || !VIM_ISWHITE(expr[2]))
{
emsg(_(e_missing_in));
return fi;
--- 1504,1512 ----
if (expr == NULL)
return fi;
! 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,1516 ****
if (skip)
++emsg_skip;
! if (eval0(skipwhite(expr + 2), &tv, eap, &evalarg) == OK)
{
*errp = FALSE;
if (!skip)
--- 1514,1521 ----
if (skip)
++emsg_skip;
! expr = skipwhite_and_linebreak(expr + 2, evalarg);
! if (eval0(expr, &tv, eap, evalarg) == OK)
{
*errp = FALSE;
if (!skip)
***************
*** 1558,1568 ****
--- 1563,1587 ----
}
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,1871 ****
--- 1885,1891 ----
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,1911 ****
void
clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
{
! if (evalarg != NULL && eap != 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;
evalarg->eval_tofree = NULL;
}
}
--- 1918,1936 ----
void
clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
{
! if (evalarg != NULL && evalarg->eval_tofree != NULL)
{
! 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,1968 ****
if (eap != NULL)
eap->nextcmd = check_nextcmd(p);
- clear_evalarg(evalarg, eap);
-
return ret;
}
--- 1986,1991 ----
*** ../vim-8.2.1079/src/ex_eval.c 2020-06-27 18:06:42.152575113 +0200
--- src/ex_eval.c 2020-06-28 17:44:22.647831198 +0200
***************
*** 899,908 ****
CLEAR_FIELD(evalarg);
evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
! evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL;
if (eval0(eap->arg, &tv, eap, &evalarg) == OK)
clear_tv(&tv);
}
/*
--- 899,914 ----
CLEAR_FIELD(evalarg);
evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
! 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,1114 ****
}
else
{
! void *fi;
/*
* ":for var in list-expr"
--- 1114,1129 ----
}
else
{
! 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,1129 ****
// previously evaluated list.
fi = cstack->cs_forinfo[cstack->cs_idx];
error = FALSE;
}
else
{
// Evaluate the argument and get the info in a structure.
! fi = eval_for_line(eap->arg, &error, eap, skip);
cstack->cs_forinfo[cstack->cs_idx] = fi;
}
--- 1134,1147 ----
// 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, &evalarg);
cstack->cs_forinfo[cstack->cs_idx] = fi;
}
***************
*** 1138,1143 ****
--- 1156,1162 ----
free_for_info(fi);
cstack->cs_forinfo[cstack->cs_idx] = NULL;
}
+ clear_evalarg(&evalarg, eap);
}
/*
*** ../vim-8.2.1079/src/proto/
eval.pro 2020-06-27 23:07:31.959995377 +0200
--- src/proto/
eval.pro 2020-06-28 17:44:27.595803900 +0200
***************
*** 22,28 ****
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);
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);
--- 22,29 ----
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, 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);
*** ../vim-8.2.1079/src/userfunc.c 2020-06-27 21:17:55.359214424 +0200
--- src/userfunc.c 2020-06-28 17:11:10.624308131 +0200
***************
*** 3825,3830 ****
--- 3825,3831 ----
if (eap->skip)
--emsg_skip;
+ clear_evalarg(&evalarg, eap);
}
/*
*** ../vim-8.2.1079/src/structs.h 2020-06-28 15:51:12.145674365 +0200
--- src/structs.h 2020-06-28 17:34:58.438997932 +0200
***************
*** 1758,1768 ****
// 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
// 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()
// Used to collect lines while parsing them, so that they can be
// concatenated later. Used when "eval_ga.ga_itemsize" is not zero.
--- 1758,1769 ----
// 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_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()
// Used to collect lines while parsing them, so that they can be
// concatenated later. Used when "eval_ga.ga_itemsize" is not zero.
*** ../vim-8.2.1079/src/globals.h 2020-06-28 15:51:12.145674365 +0200
--- src/globals.h 2020-06-28 17:35:33.170798595 +0200
***************
*** 1885,1891 ****
// 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}
# endif
;
#endif
--- 1885,1891 ----
// Passed to an eval() function to enable evaluation.
EXTERN evalarg_T EVALARG_EVALUATE
# ifdef DO_INIT
! = {EVAL_EVALUATE, 0, NULL, NULL, {0, 0, 0, 0, NULL}, NULL}
# endif
;
#endif
*** ../vim-8.2.1079/src/testdir/test_vim9_cmd.vim 2020-06-28 15:51:12.149674344 +0200
--- src/testdir/test_vim9_cmd.vim 2020-06-28 18:14:35.689151772 +0200
***************
*** 160,163 ****
--- 160,194 ----
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
*** ../vim-8.2.1079/src/version.c 2020-06-28 15:51:12.149674344 +0200
--- src/version.c 2020-06-28 17:01:23.466966315 +0200
***************
*** 756,757 ****
--- 756,759 ----
{ /* Add new patch number below this line */
+ /**/
+ 1080,
/**/
--
NEIL INNES PLAYED: THE FIRST SELF-DESTRUCTIVE MONK, ROBIN'S LEAST FAVORITE
MINSTREL, THE PAGE CRUSHED BY A RABBIT, THE OWNER OF A DUCK
"Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language --
http://www.Zimbu.org ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///