Patch 8.2.1079

3 views
Skip to first unread message

Bram Moolenaar

unread,
Jun 28, 2020, 9:51:53 AM6/28/20
to vim...@googlegroups.com

Patch 8.2.1079
Problem: Vim9: no line break allowed in a while loop.
Solution: Update stored loop lines when finding line breaks.
Files: src/structs.h, src/globals.h, src/eval.c, src/evalvars.c,
src/ex_docmd.c, src/proto/ex_docmd.pro,
src/testdir/test_vim9_cmd.vim


*** ../vim-8.2.1078/src/structs.h 2020-06-27 18:06:42.152575113 +0200
--- src/structs.h 2020-06-28 13:54:22.894023740 +0200
***************
*** 1761,1767 ****
int eval_flags; // EVAL_ flag values below

// copied from exarg_T when "getline" is "getsourceline". Can be NULL.
! void *eval_cookie; // argument for getline()

// Used to collect lines while parsing them, so that they can be
// concatenated later. Used when "eval_ga.ga_itemsize" is not zero.
--- 1761,1768 ----
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.
*** ../vim-8.2.1078/src/globals.h 2020-06-27 18:06:42.152575113 +0200
--- src/globals.h 2020-06-28 14:16:27.071259926 +0200
***************
*** 1885,1891 ****
// 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}
# endif
;
#endif
--- 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
*** ../vim-8.2.1078/src/eval.c 2020-06-27 23:07:31.959995377 +0200
--- src/eval.c 2020-06-28 14:43:43.388563492 +0200
***************
*** 170,177 ****

CLEAR_FIELD(evalarg);
evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE;
! evalarg.eval_cookie = eap != NULL && eap->getline == getsourceline
! ? eap->cookie : NULL;

if (skip)
++emsg_skip;
--- 170,180 ----

CLEAR_FIELD(evalarg);
evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE;
! 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,1849 ****
&& evalarg != NULL
&& evalarg->eval_cookie != NULL
&& (*arg == NUL || (VIM_ISWHITE(arg[-1])
! && (*arg == '"' || *arg == '#')))
! && source_nextline(evalarg->eval_cookie) != NULL)
{
! char_u *p = source_nextline(evalarg->eval_cookie);

if (p != NULL)
{
--- 1843,1851 ----
&& evalarg != NULL
&& evalarg->eval_cookie != NULL
&& (*arg == NUL || (VIM_ISWHITE(arg[-1])
! && (*arg == '"' || *arg == '#'))))
{
! char_u *p = getline_peek(evalarg->eval_getline, evalarg->eval_cookie);

if (p != NULL)
{
***************
*** 1863,1869 ****
garray_T *gap = &evalarg->eval_ga;
char_u *line;

! line = getsourceline(0, evalarg->eval_cookie, 0, TRUE);
if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK)
{
// Going to concatenate the lines after parsing.
--- 1865,1871 ----
garray_T *gap = &evalarg->eval_ga;
char_u *line;

! 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,5212 ****

CLEAR_FIELD(evalarg);
evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
! evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL;

if (eap->skip)
++emsg_skip;
--- 5208,5218 ----

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 (eap->skip)
++emsg_skip;
*** ../vim-8.2.1078/src/evalvars.c 2020-06-27 23:07:31.959995377 +0200
--- src/evalvars.c 2020-06-28 14:10:35.105052556 +0200
***************
*** 799,806 ****
++emsg_skip;
CLEAR_FIELD(evalarg);
evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
! evalarg.eval_cookie = eap->getline == getsourceline
! ? eap->cookie : NULL;
i = eval0(expr, &rettv, eap, &evalarg);
if (eap->skip)
--emsg_skip;
--- 799,809 ----
++emsg_skip;
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;
! }
i = eval0(expr, &rettv, eap, &evalarg);
if (eap->skip)
--emsg_skip;
*** ../vim-8.2.1078/src/ex_docmd.c 2020-06-26 19:44:02.972305916 +0200
--- src/ex_docmd.c 2020-06-28 15:43:31.307673246 +0200
***************
*** 629,634 ****
--- 629,635 ----
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,877 ****
}
# 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().
--- 852,857 ----
***************
*** 929,949 ****

#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
* :endwhile/:endfor.
*/
! if (current_line == lines_ga.ga_len
! && (cstack.cs_looplevel || has_loop_cmd(next_cmdline)))
{
! if (store_loop_line(&lines_ga, next_cmdline) == FAIL)
{
retval = FAIL;
break;
}
}
did_endif = FALSE;
#endif

--- 909,952 ----

#ifdef FEAT_EVAL
/*
! * 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 ((cstack.cs_looplevel > 0 || has_loop_cmd(next_cmdline)))
{
! 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,1084 ****
else if (cstack.cs_lflags & CSL_HAD_LOOP)
{
cstack.cs_lflags &= ~CSL_HAD_LOOP;
! cstack.cs_line[cstack.cs_idx] = current_line - 1;
}
}

--- 1081,1087 ----
else if (cstack.cs_lflags & CSL_HAD_LOOP)
{
cstack.cs_lflags &= ~CSL_HAD_LOOP;
! cstack.cs_line[cstack.cs_idx] = current_line_before;
}
}

***************
*** 1515,1521 ****
{
#ifdef FEAT_EVAL
char_u *(*gp)(int, void *, int, int);
! 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
--- 1518,1524 ----
{
#ifdef FEAT_EVAL
char_u *(*gp)(int, void *, int, int);
! 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,1538 ****
--- 1536,1576 ----
#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",
*** ../vim-8.2.1078/src/proto/ex_docmd.pro 2020-05-01 15:44:24.535895262 +0200
--- src/proto/ex_docmd.pro 2020-06-28 14:44:00.208488648 +0200
***************
*** 4,13 ****
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);
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);
int modifier_len(char_u *cmd);
int cmd_exists(char_u *name);
cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);
--- 4,14 ----
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);
int modifier_len(char_u *cmd);
int cmd_exists(char_u *name);
cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);
*** ../vim-8.2.1078/src/testdir/test_vim9_cmd.vim 2020-06-27 23:07:31.959995377 +0200
--- src/testdir/test_vim9_cmd.vim 2020-06-28 15:45:47.707123048 +0200
***************
*** 131,142 ****
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
endwhile
assert_equal(16, nr)
END
--- 131,159 ----
enddef

def Test_while_linebreak()
let lines =<< trim END
vim9script
let nr = 0
! 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
*** ../vim-8.2.1078/src/version.c 2020-06-28 13:17:07.551811006 +0200
--- src/version.c 2020-06-28 13:24:33.984114947 +0200
***************
*** 756,757 ****
--- 756,759 ----
{ /* Add new patch number below this line */
+ /**/
+ 1079,
/**/

--
TERRY GILLIAM PLAYED: PATSY (ARTHUR'S TRUSTY STEED), THE GREEN KNIGHT
SOOTHSAYER, BRIDGEKEEPER, SIR GAWAIN (THE FIRST TO BE
KILLED BY THE RABBIT)
"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 ///
Reply all
Reply to author
Forward
0 new messages