mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 8.2.1079: Vim9: no line break allowed in a while loop
Problem: Vim9: no line break allowed in a while loop. Solution: Update stored loop lines when finding line breaks.
This commit is contained in:
20
src/eval.c
20
src/eval.c
@@ -170,8 +170,11 @@ eval_to_bool(
|
|||||||
|
|
||||||
CLEAR_FIELD(evalarg);
|
CLEAR_FIELD(evalarg);
|
||||||
evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE;
|
evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE;
|
||||||
evalarg.eval_cookie = eap != NULL && eap->getline == getsourceline
|
if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline))
|
||||||
? eap->cookie : NULL;
|
{
|
||||||
|
evalarg.eval_getline = eap->getline;
|
||||||
|
evalarg.eval_cookie = eap->cookie;
|
||||||
|
}
|
||||||
|
|
||||||
if (skip)
|
if (skip)
|
||||||
++emsg_skip;
|
++emsg_skip;
|
||||||
@@ -1840,10 +1843,9 @@ eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext)
|
|||||||
&& evalarg != NULL
|
&& evalarg != NULL
|
||||||
&& evalarg->eval_cookie != NULL
|
&& evalarg->eval_cookie != NULL
|
||||||
&& (*arg == NUL || (VIM_ISWHITE(arg[-1])
|
&& (*arg == NUL || (VIM_ISWHITE(arg[-1])
|
||||||
&& (*arg == '"' || *arg == '#')))
|
&& (*arg == '"' || *arg == '#'))))
|
||||||
&& source_nextline(evalarg->eval_cookie) != NULL)
|
|
||||||
{
|
{
|
||||||
char_u *p = source_nextline(evalarg->eval_cookie);
|
char_u *p = getline_peek(evalarg->eval_getline, evalarg->eval_cookie);
|
||||||
|
|
||||||
if (p != NULL)
|
if (p != NULL)
|
||||||
{
|
{
|
||||||
@@ -1863,7 +1865,7 @@ eval_next_line(evalarg_T *evalarg)
|
|||||||
garray_T *gap = &evalarg->eval_ga;
|
garray_T *gap = &evalarg->eval_ga;
|
||||||
char_u *line;
|
char_u *line;
|
||||||
|
|
||||||
line = getsourceline(0, evalarg->eval_cookie, 0, TRUE);
|
line = evalarg->eval_getline(0, evalarg->eval_cookie, 0, TRUE);
|
||||||
if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK)
|
if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK)
|
||||||
{
|
{
|
||||||
// Going to concatenate the lines after parsing.
|
// Going to concatenate the lines after parsing.
|
||||||
@@ -5206,7 +5208,11 @@ ex_echo(exarg_T *eap)
|
|||||||
|
|
||||||
CLEAR_FIELD(evalarg);
|
CLEAR_FIELD(evalarg);
|
||||||
evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
|
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 (eap->skip)
|
if (eap->skip)
|
||||||
++emsg_skip;
|
++emsg_skip;
|
||||||
|
@@ -799,8 +799,11 @@ ex_let(exarg_T *eap)
|
|||||||
++emsg_skip;
|
++emsg_skip;
|
||||||
CLEAR_FIELD(evalarg);
|
CLEAR_FIELD(evalarg);
|
||||||
evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
|
evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
|
||||||
evalarg.eval_cookie = eap->getline == getsourceline
|
if (getline_equal(eap->getline, eap->cookie, getsourceline))
|
||||||
? eap->cookie : NULL;
|
{
|
||||||
|
evalarg.eval_getline = eap->getline;
|
||||||
|
evalarg.eval_cookie = eap->cookie;
|
||||||
|
}
|
||||||
i = eval0(expr, &rettv, eap, &evalarg);
|
i = eval0(expr, &rettv, eap, &evalarg);
|
||||||
if (eap->skip)
|
if (eap->skip)
|
||||||
--emsg_skip;
|
--emsg_skip;
|
||||||
|
@@ -629,6 +629,7 @@ do_cmdline(
|
|||||||
cstack_T cstack; // conditional stack
|
cstack_T cstack; // conditional stack
|
||||||
garray_T lines_ga; // keep lines for ":while"/":for"
|
garray_T lines_ga; // keep lines for ":while"/":for"
|
||||||
int current_line = 0; // active line in lines_ga
|
int current_line = 0; // active line in lines_ga
|
||||||
|
int current_line_before = 0;
|
||||||
char_u *fname = NULL; // function or script name
|
char_u *fname = NULL; // function or script name
|
||||||
linenr_T *breakpoint = NULL; // ptr to breakpoint field in cookie
|
linenr_T *breakpoint = NULL; // ptr to breakpoint field in cookie
|
||||||
int *dbg_tick = NULL; // ptr to dbg_tick field in cookie
|
int *dbg_tick = NULL; // ptr to dbg_tick field in cookie
|
||||||
@@ -851,27 +852,6 @@ do_cmdline(
|
|||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cstack.cs_looplevel > 0)
|
|
||||||
{
|
|
||||||
// Inside a while/for loop we need to store the lines and use them
|
|
||||||
// again. Pass a different "fgetline" function to do_one_cmd()
|
|
||||||
// below, so that it stores lines in or reads them from
|
|
||||||
// "lines_ga". Makes it possible to define a function inside a
|
|
||||||
// while/for loop.
|
|
||||||
cmd_getline = get_loop_line;
|
|
||||||
cmd_cookie = (void *)&cmd_loop_cookie;
|
|
||||||
cmd_loop_cookie.lines_gap = &lines_ga;
|
|
||||||
cmd_loop_cookie.current_line = current_line;
|
|
||||||
cmd_loop_cookie.getline = fgetline;
|
|
||||||
cmd_loop_cookie.cookie = cookie;
|
|
||||||
cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cmd_getline = fgetline;
|
|
||||||
cmd_cookie = cookie;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 2. If no line given, get an allocated line with fgetline().
|
// 2. If no line given, get an allocated line with fgetline().
|
||||||
@@ -929,21 +909,44 @@ do_cmdline(
|
|||||||
|
|
||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
/*
|
/*
|
||||||
* Save the current line when inside a ":while" or ":for", and when
|
* Inside a while/for loop, and when the command looks like a ":while"
|
||||||
* the command looks like a ":while" or ":for", because we may need it
|
* or ":for", the line is stored, because we may need it later when
|
||||||
* later. When there is a '|' and another command, it is stored
|
* looping.
|
||||||
* separately, because we need to be able to jump back to it from an
|
*
|
||||||
|
* When there is a '|' and another command, it is stored separately,
|
||||||
|
* because we need to be able to jump back to it from an
|
||||||
* :endwhile/:endfor.
|
* :endwhile/:endfor.
|
||||||
|
*
|
||||||
|
* Pass a different "fgetline" function to do_one_cmd() below,
|
||||||
|
* that it stores lines in or reads them from "lines_ga". Makes it
|
||||||
|
* possible to define a function inside a while/for loop and handles
|
||||||
|
* line continuation.
|
||||||
*/
|
*/
|
||||||
if (current_line == lines_ga.ga_len
|
if ((cstack.cs_looplevel > 0 || has_loop_cmd(next_cmdline)))
|
||||||
&& (cstack.cs_looplevel || has_loop_cmd(next_cmdline)))
|
|
||||||
{
|
{
|
||||||
if (store_loop_line(&lines_ga, next_cmdline) == FAIL)
|
cmd_getline = get_loop_line;
|
||||||
|
cmd_cookie = (void *)&cmd_loop_cookie;
|
||||||
|
cmd_loop_cookie.lines_gap = &lines_ga;
|
||||||
|
cmd_loop_cookie.current_line = current_line;
|
||||||
|
cmd_loop_cookie.getline = fgetline;
|
||||||
|
cmd_loop_cookie.cookie = cookie;
|
||||||
|
cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len);
|
||||||
|
|
||||||
|
// Save the current line when encountering it the first time.
|
||||||
|
if (current_line == lines_ga.ga_len
|
||||||
|
&& store_loop_line(&lines_ga, next_cmdline) == FAIL)
|
||||||
{
|
{
|
||||||
retval = FAIL;
|
retval = FAIL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
current_line_before = current_line;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmd_getline = fgetline;
|
||||||
|
cmd_cookie = cookie;
|
||||||
|
}
|
||||||
|
|
||||||
did_endif = FALSE;
|
did_endif = FALSE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1078,7 +1081,7 @@ do_cmdline(
|
|||||||
else if (cstack.cs_lflags & CSL_HAD_LOOP)
|
else if (cstack.cs_lflags & CSL_HAD_LOOP)
|
||||||
{
|
{
|
||||||
cstack.cs_lflags &= ~CSL_HAD_LOOP;
|
cstack.cs_lflags &= ~CSL_HAD_LOOP;
|
||||||
cstack.cs_line[cstack.cs_idx] = current_line - 1;
|
cstack.cs_line[cstack.cs_idx] = current_line_before;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1533,6 +1536,41 @@ getline_cookie(
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(FEAT_EVAL) || defined(PROT)
|
||||||
|
/*
|
||||||
|
* Get the next line source line without advancing.
|
||||||
|
*/
|
||||||
|
char_u *
|
||||||
|
getline_peek(
|
||||||
|
char_u *(*fgetline)(int, void *, int, int) UNUSED,
|
||||||
|
void *cookie) // argument for fgetline()
|
||||||
|
{
|
||||||
|
char_u *(*gp)(int, void *, int, int);
|
||||||
|
struct loop_cookie *cp;
|
||||||
|
wcmd_T *wp;
|
||||||
|
|
||||||
|
// When "fgetline" is "get_loop_line()" use the "cookie" to find the
|
||||||
|
// cookie that's originally used to obtain the lines. This may be nested
|
||||||
|
// several levels.
|
||||||
|
gp = fgetline;
|
||||||
|
cp = (struct loop_cookie *)cookie;
|
||||||
|
while (gp == get_loop_line)
|
||||||
|
{
|
||||||
|
if (cp->current_line + 1 < cp->lines_gap->ga_len)
|
||||||
|
{
|
||||||
|
// executing lines a second time, use the stored copy
|
||||||
|
wp = (wcmd_T *)(cp->lines_gap->ga_data) + cp->current_line + 1;
|
||||||
|
return wp->line;
|
||||||
|
}
|
||||||
|
gp = cp->getline;
|
||||||
|
cp = cp->cookie;
|
||||||
|
}
|
||||||
|
if (gp == getsourceline)
|
||||||
|
return source_nextline(cp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper function to apply an offset for buffer commands, i.e. ":bdelete",
|
* Helper function to apply an offset for buffer commands, i.e. ":bdelete",
|
||||||
|
@@ -1885,7 +1885,7 @@ EXTERN listitem_T range_list_item;
|
|||||||
// Passed to an eval() function to enable evaluation.
|
// Passed to an eval() function to enable evaluation.
|
||||||
EXTERN evalarg_T EVALARG_EVALUATE
|
EXTERN evalarg_T EVALARG_EVALUATE
|
||||||
# ifdef DO_INIT
|
# ifdef DO_INIT
|
||||||
= {EVAL_EVALUATE, NULL, {0, 0, 0, 0, NULL}, NULL}
|
= {EVAL_EVALUATE, NULL, NULL, {0, 0, 0, 0, NULL}, NULL}
|
||||||
# endif
|
# endif
|
||||||
;
|
;
|
||||||
#endif
|
#endif
|
||||||
|
@@ -4,10 +4,11 @@ int do_cmdline_cmd(char_u *cmd);
|
|||||||
int do_cmdline(char_u *cmdline, char_u *(*fgetline)(int, void *, int, int), void *cookie, int flags);
|
int do_cmdline(char_u *cmdline, char_u *(*fgetline)(int, void *, int, int), void *cookie, int flags);
|
||||||
int getline_equal(char_u *(*fgetline)(int, void *, int, int), void *cookie, char_u *(*func)(int, void *, int, int));
|
int getline_equal(char_u *(*fgetline)(int, void *, int, int), void *cookie, char_u *(*func)(int, void *, int, int));
|
||||||
void *getline_cookie(char_u *(*fgetline)(int, void *, int, int), void *cookie);
|
void *getline_cookie(char_u *(*fgetline)(int, void *, int, int), void *cookie);
|
||||||
|
char_u *getline_peek(char_u *(*fgetline)(int, void *, int, int), void *cookie);
|
||||||
int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only);
|
int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only);
|
||||||
int parse_cmd_address(exarg_T *eap, char **errormsg, int silent);
|
int parse_cmd_address(exarg_T *eap, char **errormsg, int silent);
|
||||||
int checkforcmd(char_u **pp, char *cmd, int len);
|
int checkforcmd(char_u **pp, char *cmd, int len);
|
||||||
char_u *find_ex_command(exarg_T *eap, int *full, void *((*lookup)(char_u *, size_t, cctx_T *)), cctx_T *cctx);
|
char_u *find_ex_command(exarg_T *eap, int *full, void *(*lookup)(char_u *, size_t, cctx_T *), cctx_T *cctx);
|
||||||
int modifier_len(char_u *cmd);
|
int modifier_len(char_u *cmd);
|
||||||
int cmd_exists(char_u *name);
|
int cmd_exists(char_u *name);
|
||||||
cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);
|
cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);
|
||||||
|
@@ -1761,7 +1761,8 @@ typedef struct {
|
|||||||
int eval_flags; // EVAL_ flag values below
|
int eval_flags; // EVAL_ flag values below
|
||||||
|
|
||||||
// copied from exarg_T when "getline" is "getsourceline". Can be NULL.
|
// copied from exarg_T when "getline" is "getsourceline". Can be NULL.
|
||||||
void *eval_cookie; // argument for getline()
|
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
|
// Used to collect lines while parsing them, so that they can be
|
||||||
// concatenated later. Used when "eval_ga.ga_itemsize" is not zero.
|
// concatenated later. Used when "eval_ga.ga_itemsize" is not zero.
|
||||||
|
@@ -131,12 +131,29 @@ def Test_if_linebreak()
|
|||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_while_linebreak()
|
def Test_while_linebreak()
|
||||||
" TODO: line break in :while expression doesn't work yet
|
|
||||||
let lines =<< trim END
|
let lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
let nr = 0
|
let nr = 0
|
||||||
while nr < 10 + 3
|
while nr <
|
||||||
nr = nr + 4
|
10 + 3
|
||||||
|
nr = nr
|
||||||
|
+ 4
|
||||||
|
endwhile
|
||||||
|
assert_equal(16, nr)
|
||||||
|
END
|
||||||
|
CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
let nr = 0
|
||||||
|
while nr
|
||||||
|
<
|
||||||
|
10
|
||||||
|
+
|
||||||
|
3
|
||||||
|
nr = nr
|
||||||
|
+
|
||||||
|
4
|
||||||
endwhile
|
endwhile
|
||||||
assert_equal(16, nr)
|
assert_equal(16, nr)
|
||||||
END
|
END
|
||||||
|
@@ -754,6 +754,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1079,
|
||||||
/**/
|
/**/
|
||||||
1078,
|
1078,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user