summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-06-28 15:51:16 +0200
committerBram Moolenaar <Bram@vim.org>2020-06-28 15:51:16 +0200
commitd5053d015a957b343ad9c9e45e0abd2978f10cf0 (patch)
tree4154d4cd881536a310adec7425bcb9e1c05da85d
parent06cf97e714fd8bf9b35ff5f8a6f2302c79acdd03 (diff)
downloadvim-git-d5053d015a957b343ad9c9e45e0abd2978f10cf0.tar.gz
patch 8.2.1079: Vim9: no line break allowed in a while loopv8.2.1079
Problem: Vim9: no line break allowed in a while loop. Solution: Update stored loop lines when finding line breaks.
-rw-r--r--src/eval.c20
-rw-r--r--src/evalvars.c7
-rw-r--r--src/ex_docmd.c98
-rw-r--r--src/globals.h2
-rw-r--r--src/proto/ex_docmd.pro3
-rw-r--r--src/structs.h3
-rw-r--r--src/testdir/test_vim9_cmd.vim23
-rw-r--r--src/version.c2
8 files changed, 113 insertions, 45 deletions
diff --git a/src/eval.c b/src/eval.c
index 6aaa3a6ef..82045675d 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -170,8 +170,11 @@ eval_to_bool(
CLEAR_FIELD(evalarg);
evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE;
- evalarg.eval_cookie = eap != NULL && eap->getline == getsourceline
- ? eap->cookie : NULL;
+ if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline))
+ {
+ evalarg.eval_getline = eap->getline;
+ evalarg.eval_cookie = eap->cookie;
+ }
if (skip)
++emsg_skip;
@@ -1840,10 +1843,9 @@ eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext)
&& evalarg != NULL
&& evalarg->eval_cookie != NULL
&& (*arg == NUL || (VIM_ISWHITE(arg[-1])
- && (*arg == '"' || *arg == '#')))
- && source_nextline(evalarg->eval_cookie) != NULL)
+ && (*arg == '"' || *arg == '#'))))
{
- char_u *p = source_nextline(evalarg->eval_cookie);
+ char_u *p = getline_peek(evalarg->eval_getline, evalarg->eval_cookie);
if (p != NULL)
{
@@ -1863,7 +1865,7 @@ eval_next_line(evalarg_T *evalarg)
garray_T *gap = &evalarg->eval_ga;
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)
{
// Going to concatenate the lines after parsing.
@@ -5206,7 +5208,11 @@ ex_echo(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 (eap->skip)
++emsg_skip;
diff --git a/src/evalvars.c b/src/evalvars.c
index 1d6172ac7..901d406a0 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -799,8 +799,11 @@ ex_let(exarg_T *eap)
++emsg_skip;
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;
+ }
i = eval0(expr, &rettv, eap, &evalarg);
if (eap->skip)
--emsg_skip;
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 2469df341..0ec63e24e 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -629,6 +629,7 @@ do_cmdline(
cstack_T cstack; // conditional stack
garray_T lines_ga; // keep lines for ":while"/":for"
int current_line = 0; // active line in lines_ga
+ int current_line_before = 0;
char_u *fname = NULL; // function or script name
linenr_T *breakpoint = NULL; // ptr to breakpoint field in cookie
int *dbg_tick = NULL; // ptr to dbg_tick field in cookie
@@ -851,27 +852,6 @@ do_cmdline(
}
# 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
// 2. If no line given, get an allocated line with fgetline().
@@ -929,21 +909,44 @@ do_cmdline(
#ifdef FEAT_EVAL
/*
- * Save the current line when inside a ":while" or ":for", and when
- * the command looks like a ":while" or ":for", because we may need it
- * later. When there is a '|' and another command, it is stored
- * separately, because we need to be able to jump back to it from an
+ * Inside a while/for loop, and when the command looks like a ":while"
+ * or ":for", the line is stored, because we may need it later when
+ * looping.
+ *
+ * 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.
+ *
+ * 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
- && (cstack.cs_looplevel || has_loop_cmd(next_cmdline)))
+ if ((cstack.cs_looplevel > 0 || 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;
break;
}
+ current_line_before = current_line;
+ }
+ else
+ {
+ cmd_getline = fgetline;
+ cmd_cookie = cookie;
}
+
did_endif = FALSE;
#endif
@@ -1078,7 +1081,7 @@ do_cmdline(
else if (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;
}
}
@@ -1515,7 +1518,7 @@ getline_cookie(
{
#ifdef FEAT_EVAL
char_u *(*gp)(int, void *, int, int);
- struct loop_cookie *cp;
+ struct loop_cookie *cp;
// 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
@@ -1533,6 +1536,41 @@ getline_cookie(
#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",
diff --git a/src/globals.h b/src/globals.h
index 81903818b..19c6837ce 100644
--- 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, {0, 0, 0, 0, NULL}, NULL}
+ = {EVAL_EVALUATE, NULL, NULL, {0, 0, 0, 0, NULL}, NULL}
# endif
;
#endif
diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro
index 7f5c69128..8f5ac10d4 100644
--- a/src/proto/ex_docmd.pro
+++ b/src/proto/ex_docmd.pro
@@ -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 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);
+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_cmd_address(exarg_T *eap, char **errormsg, int silent);
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 cmd_exists(char_u *name);
cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);
diff --git a/src/structs.h b/src/structs.h
index d68bc0184..fd5ae2aa2 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1761,7 +1761,8 @@ typedef struct {
int eval_flags; // EVAL_ flag values below
// 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
// 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
index a818d1408..c0a4358d8 100644
--- a/src/testdir/test_vim9_cmd.vim
+++ b/src/testdir/test_vim9_cmd.vim
@@ -131,12 +131,29 @@ def Test_if_linebreak()
enddef
def Test_while_linebreak()
- " TODO: line break in :while expression doesn't work yet
let lines =<< trim END
vim9script
let nr = 0
- while nr < 10 + 3
- nr = nr + 4
+ while nr <
+ 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
assert_equal(16, nr)
END
diff --git a/src/version.c b/src/version.c
index 4394d52b3..8047cbe68 100644
--- 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 */
/**/
+ 1079,
+/**/
1078,
/**/
1077,