changeset 25469:dcd45fe7fe2e v8.2.3271

patch 8.2.3271: Vim9: cannot use :command or :au with block in :def function Commit: https://github.com/vim/vim/commit/e4db17fb6e2d029aa2dddfca703ace9bcf0d85fd Author: Bram Moolenaar <Bram@vim.org> Date: Sun Aug 1 21:19:43 2021 +0200 patch 8.2.3271: Vim9: cannot use :command or :au with block in :def function Problem: Vim9: cannot use :command or :au with a block in a :def function. Solution: Recognize the start of the block.
author Bram Moolenaar <Bram@vim.org>
date Sun, 01 Aug 2021 21:30:03 +0200
parents 0290badbbf7b
children 85b99764bb66
files src/ex_docmd.c src/proto/ex_docmd.pro src/testdir/test_vim9_script.vim src/usercmd.c src/userfunc.c src/version.c src/vim9compile.c
diffstat 7 files changed, 99 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -2741,7 +2741,7 @@ checkforcmd(
  * 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
+    int
 checkforcmd_noparen(
     char_u	**pp,		// start of command
     char	*cmd,		// name of command
--- a/src/proto/ex_docmd.pro
+++ b/src/proto/ex_docmd.pro
@@ -9,6 +9,7 @@ void *getline_cookie(char_u *(*fgetline)
 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);
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -334,6 +334,34 @@ def Test_block_local_vars_with_func()
   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
--- a/src/usercmd.c
+++ b/src/usercmd.c
@@ -983,29 +983,32 @@ may_get_cmd_block(exarg_T *eap, char_u *
     if (*p == '{' && ends_excmd2(eap->arg, skipwhite(p + 1))
 						       && eap->getline != NULL)
     {
-	garray_T	ga;
-	char_u	*line = 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)
+	// 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 (;;)
 	    {
-		emsg(_(e_missing_rcurly));
-		break;
+		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;
 	    }
-	    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);
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -903,12 +903,25 @@ get_function_body(
 		    --end;
 		if (end > p && *end == '{')
 		{
+		    int	    is_block;
+
+		    // check for trailing "=> {": start of an inline function
 		    --end;
 		    while (end > p && VIM_ISWHITE(*end))
 			--end;
-		    if (end > p + 2 && end[-1] == '=' && end[0] == '>')
+		    is_block = end > p + 2 && end[-1] == '=' && end[0] == '>';
+		    if (!is_block)
 		    {
-			// found trailing "=> {", start of an inline function
+			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
--- a/src/version.c
+++ b/src/version.c
@@ -756,6 +756,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3271,
+/**/
     3270,
 /**/
     3269,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -8861,11 +8861,13 @@ compile_put(char_u *arg, exarg_T *eap, c
  * A command that is not compiled, execute with legacy code.
  */
     static char_u *
-compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx)
-{
+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,6 +8924,34 @@ compile_exec(char_u *line, exarg_T *eap,
 		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,6 +9038,7 @@ theend:
 	--nextcmd;
 	*nextcmd = '|';
     }
+    vim_free(tofree);
 
     return nextcmd;
 }