]> granicus.if.org Git - vim/commitdiff
patch 8.2.3271: Vim9: cannot use :command or :au with block in :def function v8.2.3271
authorBram Moolenaar <Bram@vim.org>
Sun, 1 Aug 2021 19:19:43 +0000 (21:19 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 1 Aug 2021 19:19:43 +0000 (21:19 +0200)
Problem:    Vim9: cannot use :command or :au with a block in a :def function.
Solution:   Recognize the start of the block.

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

index f2ff787e082006e2492e4632ee38bf83e25696ce..fde68be723aab85b6469c63ed1bf4ce35dbf3386 100644 (file)
@@ -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
index d22d40689cc72ed59b2211d303c860af54c8ae7a..419d00457cb4ce758294126a28724b6934574845 100644 (file)
@@ -9,6 +9,7 @@ void *getline_cookie(char_u *(*fgetline)(int, void *, int, getline_opt_T), void
 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);
index 4b9e6f3065939420b0becfe627cbba768d0a7254..9f99ebbd4449980f2265b0de9819716b40df8056 100644 (file)
@@ -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
index 48f84b1c3b21846da18de7a4414d49210068421e..41f8e0476888e917501b457a1d37aec9cf36181c 100644 (file)
@@ -983,29 +983,32 @@ may_get_cmd_block(exarg_T *eap, char_u *p, char_u **tofree, int *flags)
     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);
index 045692c7f1ebc036bc4216b64f0b542bdca2f0e3..01c44d44018fbcd4699593255aaaad3b92ba5951 100644 (file)
@@ -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)
+                   {
+                       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)
                    {
-                       // found trailing "=> {", start of an inline function
                        if (nesting == MAX_FUNC_NESTING - 1)
                            emsg(_(e_function_nesting_too_deep));
                        else
index 3f2266361245ca6bff8dc81a37aea5f9b11de142..de4d26bd5e155f54a49e01472aac8910afdd54c1 100644 (file)
@@ -755,6 +755,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3271,
 /**/
     3270,
 /**/
index 9a534f46c261cdbb8ac8ac8eb025e7b35b6e93b5..8e7be28f7a35b84cfd62fb9b54674d12e08c5298 100644 (file)
@@ -8861,11 +8861,13 @@ compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx)
  * 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, cctx_T *cctx)
                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;
 }