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 -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202108011920.171JKBaD327425%40masaka.moolenaar.net.

Raspunde prin e-mail lui