To: vim_dev@googlegroups.com Subject: Patch 8.2.1079 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 -- Bram@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 ///