To: vim_dev@googlegroups.com Subject: Patch 8.2.3271 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3271 Problem: Vim9: cannot use :command or :au with a block in a :def function. Solution: Recognize the start of the block. Files: src/userfunc.c, src/usercmd.c, src/ex_docmd.c, src/proto/ex_docmd.pro, src/vim9compile.c, src/testdir/test_vim9_script.vim *** ../vim-8.2.3270/src/userfunc.c 2021-07-28 22:21:20.372610767 +0200 --- src/userfunc.c 2021-08-01 20:29:23.124748707 +0200 *************** *** 903,914 **** --end; if (end > p && *end == '{') { --end; while (end > p && VIM_ISWHITE(*end)) --end; ! if (end > p + 2 && end[-1] == '=' && end[0] == '>') { - // found trailing "=> {", start of an inline function if (nesting == MAX_FUNC_NESTING - 1) emsg(_(e_function_nesting_too_deep)); else --- 903,927 ---- --end; if (end > p && *end == '{') { + int is_block; + + // check for trailing "=> {": start of an inline function --end; while (end > p && VIM_ISWHITE(*end)) --end; ! is_block = end > p + 2 && end[-1] == '=' && end[0] == '>'; ! if (!is_block) ! { ! char_u *s = p; ! ! // check for line starting with "au" for :autocmd or ! // "com" for :command, these can use a {} block ! is_block = checkforcmd_noparen(&s, "autocmd", 2) ! || checkforcmd_noparen(&s, "command", 3); ! } ! ! if (is_block) { if (nesting == MAX_FUNC_NESTING - 1) emsg(_(e_function_nesting_too_deep)); else *** ../vim-8.2.3270/src/usercmd.c 2021-08-01 14:52:05.558645405 +0200 --- src/usercmd.c 2021-08-01 21:02:45.896382426 +0200 *************** *** 983,1011 **** if (*p == '{' && ends_excmd2(eap->arg, skipwhite(p + 1)) && eap->getline != NULL) { ! garray_T ga; ! char_u *line = NULL; ga_init2(&ga, sizeof(char_u *), 10); if (ga_add_string(&ga, p) == FAIL) return retp; ! // Read lines between '{' and '}'. Does not support nesting or ! // here-doc constructs. ! for (;;) ! { ! vim_free(line); ! if ((line = eap->getline(':', eap->cookie, ! 0, GETLINE_CONCAT_CONTBAR)) == NULL) { ! emsg(_(e_missing_rcurly)); ! break; } - if (ga_add_string(&ga, line) == FAIL) - break; - if (*skipwhite(line) == '}') - break; - } vim_free(line); retp = *tofree = ga_concat_strings(&ga, "\n"); ga_clear_strings(&ga); --- 983,1014 ---- if (*p == '{' && ends_excmd2(eap->arg, skipwhite(p + 1)) && eap->getline != NULL) { ! garray_T ga; ! char_u *line = NULL; ga_init2(&ga, sizeof(char_u *), 10); if (ga_add_string(&ga, p) == FAIL) return retp; ! // If the argument ends in "}" it must have been concatenated already ! // for ISN_EXEC. ! if (p[STRLEN(p) - 1] != '}') ! // Read lines between '{' and '}'. Does not support nesting or ! // here-doc constructs. ! for (;;) { ! vim_free(line); ! if ((line = eap->getline(':', eap->cookie, ! 0, GETLINE_CONCAT_CONTBAR)) == NULL) ! { ! emsg(_(e_missing_rcurly)); ! break; ! } ! if (ga_add_string(&ga, line) == FAIL) ! break; ! if (*skipwhite(line) == '}') ! break; } vim_free(line); retp = *tofree = ga_concat_strings(&ga, "\n"); ga_clear_strings(&ga); *** ../vim-8.2.3270/src/ex_docmd.c 2021-08-01 14:52:05.558645405 +0200 --- src/ex_docmd.c 2021-08-01 20:30:22.240600908 +0200 *************** *** 2741,2747 **** * Check for an Ex command with optional tail, not followed by "(". * If there is a match advance "pp" to the argument and return TRUE. */ ! static int checkforcmd_noparen( char_u **pp, // start of command char *cmd, // name of command --- 2741,2747 ---- * Check for an Ex command with optional tail, not followed by "(". * If there is a match advance "pp" to the argument and return TRUE. */ ! int checkforcmd_noparen( char_u **pp, // start of command char *cmd, // name of command *** ../vim-8.2.3270/src/proto/ex_docmd.pro 2021-07-31 21:32:27.425666716 +0200 --- src/proto/ex_docmd.pro 2021-08-01 20:30:44.516568164 +0200 *************** *** 9,14 **** --- 9,15 ---- char_u *getline_peek(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie); char *ex_errmsg(char *msg, char_u *arg); int checkforcmd(char_u **pp, char *cmd, int len); + int checkforcmd_noparen(char_u **pp, char *cmd, int len); int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, int skip_only); int has_cmdmod(cmdmod_T *cmod); int cmdmod_error(void); *** ../vim-8.2.3270/src/vim9compile.c 2021-08-01 13:17:12.862422853 +0200 --- src/vim9compile.c 2021-08-01 20:58:49.660947469 +0200 *************** *** 8861,8871 **** * A command that is not compiled, execute with legacy code. */ static char_u * ! compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx) { char_u *p; int has_expr = FALSE; char_u *nextcmd = (char_u *)""; if (cctx->ctx_skip == SKIP_YES) goto theend; --- 8861,8873 ---- * A command that is not compiled, execute with legacy code. */ static char_u * ! compile_exec(char_u *line_arg, exarg_T *eap, cctx_T *cctx) { + char_u *line = line_arg; char_u *p; int has_expr = FALSE; char_u *nextcmd = (char_u *)""; + char_u *tofree = NULL; if (cctx->ctx_skip == SKIP_YES) goto theend; *************** *** 8922,8927 **** --- 8924,8957 ---- nextcmd = p + 1; } } + else if (eap->cmdidx == CMD_command || eap->cmdidx == CMD_autocmd) + { + // If there is a trailing '{' read lines until the '}' + p = eap->arg + STRLEN(eap->arg) - 1; + while (p > eap->arg && VIM_ISWHITE(*p)) + --p; + if (*p == '{') + { + exarg_T ea; + int flags; // unused + int start_lnum = SOURCING_LNUM; + + CLEAR_FIELD(ea); + ea.arg = eap->arg; + fill_exarg_from_cctx(&ea, cctx); + (void)may_get_cmd_block(&ea, p, &tofree, &flags); + if (tofree != NULL) + { + *p = NUL; + line = concat_str(line, tofree); + if (line == NULL) + goto theend; + vim_free(tofree); + tofree = line; + SOURCING_LNUM = start_lnum; + } + } + } } if (eap->cmdidx == CMD_syntax && STRNCMP(eap->arg, "include ", 8) == 0) *************** *** 9008,9013 **** --- 9038,9044 ---- --nextcmd; *nextcmd = '|'; } + vim_free(tofree); return nextcmd; } *** ../vim-8.2.3270/src/testdir/test_vim9_script.vim 2021-07-29 22:48:50.107129898 +0200 --- src/testdir/test_vim9_script.vim 2021-08-01 21:16:49.682883591 +0200 *************** *** 334,339 **** --- 334,367 ---- CheckScriptSuccess(lines) enddef + " legacy func for command that's defined later + func InvokeSomeCommand() + SomeCommand + endfunc + + def Test_autocommand_block() + com SomeCommand { + g:someVar = 'some' + } + InvokeSomeCommand() + assert_equal('some', g:someVar) + + delcommand SomeCommand + unlet g:someVar + enddef + + def Test_command_block() + au BufNew *.xml { + g:otherVar = 'other' + } + split other.xml + assert_equal('other', g:otherVar) + + bwipe! + au! BufNew *.xml + unlet g:otherVar + enddef + func g:NoSuchFunc() echo 'none' endfunc *** ../vim-8.2.3270/src/version.c 2021-08-01 19:28:11.167063929 +0200 --- src/version.c 2021-08-01 20:23:37.797647003 +0200 *************** *** 757,758 **** --- 757,760 ---- { /* Add new patch number below this line */ + /**/ + 3271, /**/ -- Just think of all the things we haven't thought of yet. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///