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 -- Br...@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 ///