Patch 8.2.0717
Problem:    Vim9: postponed constant expressions does not scale.
Solution:   Add a structure to pass around postponed constants.
Files:      sr/vim9compile.c, src/testdir/test_vim9_disassemble.vim


*** ../vim-8.2.0716/src/vim9compile.c   2020-05-07 23:20:17.607439483 +0200
--- src/vim9compile.c   2020-05-08 19:02:40.415646972 +0200
***************
*** 1041,1091 ****
  }
  
  /*
-  * Generate a PUSH instruction for "tv".
-  * "tv" will be consumed or cleared.  "tv" may be NULL;
-  */
-     static int
- generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
- {
-     if (tv != NULL)
-     {
-       switch (tv->v_type)
-       {
-           case VAR_UNKNOWN:
-               break;
-           case VAR_BOOL:
-               generate_PUSHBOOL(cctx, tv->vval.v_number);
-               break;
-           case VAR_SPECIAL:
-               generate_PUSHSPEC(cctx, tv->vval.v_number);
-               break;
-           case VAR_NUMBER:
-               generate_PUSHNR(cctx, tv->vval.v_number);
-               break;
- #ifdef FEAT_FLOAT
-           case VAR_FLOAT:
-               generate_PUSHF(cctx, tv->vval.v_float);
-               break;
- #endif
-           case VAR_BLOB:
-               generate_PUSHBLOB(cctx, tv->vval.v_blob);
-               tv->vval.v_blob = NULL;
-               break;
-           case VAR_STRING:
-               generate_PUSHS(cctx, tv->vval.v_string);
-               tv->vval.v_string = NULL;
-               break;
-           default:
-               iemsg("constant type not supported");
-               clear_tv(tv);
-               return FAIL;
-       }
-       tv->v_type = VAR_UNKNOWN;
-     }
-     return OK;
- }
- 
- /*
   * Generate an ISN_STORE instruction.
   */
      static int
--- 1041,1046 ----
***************
*** 3671,3676 ****
--- 3626,3716 ----
      return OK;
  }
  
+ // Structure passed between the compile_expr* functions to keep track of
+ // constants that have been parsed but for which no code was produced yet.  If
+ // possible expressions on these constants are applied at compile time.  If
+ // that is not possible, the code to push the constants needs to be generated
+ // before other instructions.
+ typedef struct {
+     typval_T  pp_tv[10];      // stack of ppconst constants
+     int               pp_used;        // active entries in pp_tv[]
+ } ppconst_T;
+ 
+ /*
+  * Generate a PUSH instruction for "tv".
+  * "tv" will be consumed or cleared.
+  * Nothing happens if "tv" is NULL or of type VAR_UNKNOWN;
+  */
+     static int
+ generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
+ {
+     if (tv != NULL)
+     {
+       switch (tv->v_type)
+       {
+           case VAR_UNKNOWN:
+               break;
+           case VAR_BOOL:
+               generate_PUSHBOOL(cctx, tv->vval.v_number);
+               break;
+           case VAR_SPECIAL:
+               generate_PUSHSPEC(cctx, tv->vval.v_number);
+               break;
+           case VAR_NUMBER:
+               generate_PUSHNR(cctx, tv->vval.v_number);
+               break;
+ #ifdef FEAT_FLOAT
+           case VAR_FLOAT:
+               generate_PUSHF(cctx, tv->vval.v_float);
+               break;
+ #endif
+           case VAR_BLOB:
+               generate_PUSHBLOB(cctx, tv->vval.v_blob);
+               tv->vval.v_blob = NULL;
+               break;
+           case VAR_STRING:
+               generate_PUSHS(cctx, tv->vval.v_string);
+               tv->vval.v_string = NULL;
+               break;
+           default:
+               iemsg("constant type not supported");
+               clear_tv(tv);
+               return FAIL;
+       }
+       tv->v_type = VAR_UNKNOWN;
+     }
+     return OK;
+ }
+ 
+ /*
+  * Generate code for any ppconst entries.
+  */
+     static int
+ generate_ppconst(cctx_T *cctx, ppconst_T *ppconst)
+ {
+     int           i;
+     int           ret = OK;
+ 
+     for (i = 0; i < ppconst->pp_used; ++i)
+       if (generate_tv_PUSH(cctx, &ppconst->pp_tv[i]) == FAIL)
+           ret = FAIL;
+     ppconst->pp_used = 0;
+     return ret;
+ }
+ 
+ /*
+  * Clear ppconst constants.  Used when failing.
+  */
+     static void
+ clear_ppconst(ppconst_T *ppconst)
+ {
+     int           i;
+ 
+     for (i = 0; i < ppconst->pp_used; ++i)
+       clear_tv(&ppconst->pp_tv[i]);
+     ppconst->pp_used = 0;
+ }
+ 
  /*
   * Compile code to apply '-', '+' and '!'.
   */
***************
*** 3728,3736 ****
        cctx_T *cctx,
        char_u **start_leader,
        char_u *end_leader,
!       typval_T *bef1_tv,
!       typval_T *bef2_tv,
!       typval_T *new_tv)
  {
      for (;;)
      {
--- 3768,3774 ----
        cctx_T *cctx,
        char_u **start_leader,
        char_u *end_leader,
!       ppconst_T *ppconst)
  {
      for (;;)
      {
***************
*** 3740,3748 ****
            type_T      *type;
            int         argcount = 0;
  
!           if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
!                   || generate_tv_PUSH(cctx, bef2_tv) == FAIL
!                   || generate_tv_PUSH(cctx, new_tv) == FAIL)
                return FAIL;
  
            // funcref(arg)
--- 3778,3784 ----
            type_T      *type;
            int         argcount = 0;
  
!           if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
  
            // funcref(arg)
***************
*** 3758,3766 ****
        {
            char_u *p;
  
!           if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
!                   || generate_tv_PUSH(cctx, bef2_tv) == FAIL
!                   || generate_tv_PUSH(cctx, new_tv) == FAIL)
                return FAIL;
  
            // something->method()
--- 3794,3800 ----
        {
            char_u *p;
  
!           if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
  
            // something->method()
***************
*** 3800,3808 ****
            garray_T    *stack;
            type_T      **typep;
  
!           if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
!                   || generate_tv_PUSH(cctx, bef2_tv) == FAIL
!                   || generate_tv_PUSH(cctx, new_tv) == FAIL)
                return FAIL;
  
            // list index: list[123]
--- 3834,3840 ----
            garray_T    *stack;
            type_T      **typep;
  
!           if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
  
            // list index: list[123]
***************
*** 3835,3843 ****
        {
            char_u *p;
  
!           if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
!                   || generate_tv_PUSH(cctx, bef2_tv) == FAIL
!                   || generate_tv_PUSH(cctx, new_tv) == FAIL)
                return FAIL;
  
            ++*arg;
--- 3867,3873 ----
        {
            char_u *p;
  
!           if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
  
            ++*arg;
***************
*** 3871,3878 ****
   * Compile an expression at "*arg" and add instructions to "cctx->ctx_instr".
   * "arg" is advanced until after the expression, skipping white space.
   *
!  * If the value is a constant "new_tv" will be set.
!  * Before instructions are generated, any "bef_tv" will generated.
   *
   * This is the compiling equivalent of eval1(), eval2(), etc.
   */
--- 3901,3908 ----
   * Compile an expression at "*arg" and add instructions to "cctx->ctx_instr".
   * "arg" is advanced until after the expression, skipping white space.
   *
!  * If the value is a constant "ppconst->pp_ret" will be set.
!  * Before instructions are generated, any values in "ppconst" will generated.
   *
   * This is the compiling equivalent of eval1(), eval2(), etc.
   */
***************
*** 3905,3917 ****
  compile_expr7(
        char_u **arg,
        cctx_T *cctx,
!       typval_T *bef1_tv,
!       typval_T *bef2_tv,
!       typval_T *new_tv)
  {
-     typval_T  rettv;
      char_u    *start_leader, *end_leader;
      int               ret = OK;
  
      /*
       * Skip '!', '-' and '+' characters.  They are handled later.
--- 3935,3945 ----
  compile_expr7(
        char_u **arg,
        cctx_T *cctx,
!       ppconst_T *ppconst)
  {
      char_u    *start_leader, *end_leader;
      int               ret = OK;
+     typval_T  *rettv = &ppconst->pp_tv[ppconst->pp_used];
  
      /*
       * Skip '!', '-' and '+' characters.  They are handled later.
***************
*** 3921,3927 ****
        *arg = skipwhite(*arg + 1);
      end_leader = *arg;
  
!     rettv.v_type = VAR_UNKNOWN;
      switch (**arg)
      {
        /*
--- 3949,3955 ----
        *arg = skipwhite(*arg + 1);
      end_leader = *arg;
  
!     rettv->v_type = VAR_UNKNOWN;
      switch (**arg)
      {
        /*
***************
*** 3937,3964 ****
        case '7':
        case '8':
        case '9':
!       case '.':   if (get_number_tv(arg, &rettv, TRUE, FALSE) == FAIL)
                        return FAIL;
                    break;
  
        /*
         * String constant: "string".
         */
!       case '"':   if (get_string_tv(arg, &rettv, TRUE) == FAIL)
                        return FAIL;
                    break;
  
        /*
         * Literal string constant: 'str''ing'.
         */
!       case '\'':  if (get_lit_string_tv(arg, &rettv, TRUE) == FAIL)
                        return FAIL;
                    break;
  
        /*
         * Constant Vim variable.
         */
!       case 'v':   get_vim_constant(arg, &rettv);
                    ret = NOTDONE;
                    break;
  
--- 3965,3992 ----
        case '7':
        case '8':
        case '9':
!       case '.':   if (get_number_tv(arg, rettv, TRUE, FALSE) == FAIL)
                        return FAIL;
                    break;
  
        /*
         * String constant: "string".
         */
!       case '"':   if (get_string_tv(arg, rettv, TRUE) == FAIL)
                        return FAIL;
                    break;
  
        /*
         * Literal string constant: 'str''ing'.
         */
!       case '\'':  if (get_lit_string_tv(arg, rettv, TRUE) == FAIL)
                        return FAIL;
                    break;
  
        /*
         * Constant Vim variable.
         */
!       case 'v':   get_vim_constant(arg, rettv);
                    ret = NOTDONE;
                    break;
  
***************
*** 4036,4061 ****
      if (ret == FAIL)
        return FAIL;
  
!     if (rettv.v_type != VAR_UNKNOWN)
      {
        // apply the '!', '-' and '+' before the constant
!       if (apply_leader(&rettv, start_leader, end_leader) == FAIL)
        {
!           clear_tv(&rettv);
            return FAIL;
        }
        start_leader = end_leader;   // don't apply again below
  
!       // A constant expression can possibly be handled compile time.
!       *new_tv = rettv;
      }
      else if (ret == NOTDONE)
      {
        char_u      *p;
        int         r;
  
!       if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
!               || generate_tv_PUSH(cctx, bef2_tv) == FAIL)
            return FAIL;
  
        if (!eval_isnamec1(**arg))
--- 4064,4089 ----
      if (ret == FAIL)
        return FAIL;
  
!     if (rettv->v_type != VAR_UNKNOWN)
      {
        // apply the '!', '-' and '+' before the constant
!       if (apply_leader(rettv, start_leader, end_leader) == FAIL)
        {
!           clear_tv(rettv);
            return FAIL;
        }
        start_leader = end_leader;   // don't apply again below
  
!       // A constant expression can possibly be handled compile time, return
!       // the value instead of generating code.
!       ++ppconst->pp_used;
      }
      else if (ret == NOTDONE)
      {
        char_u      *p;
        int         r;
  
!       if (generate_ppconst(cctx, ppconst) == FAIL)
            return FAIL;
  
        if (!eval_isnamec1(**arg))
***************
*** 4077,4088 ****
      // Handle following "[]", ".member", etc.
      // Then deal with prefixed '-', '+' and '!', if not done already.
      if (compile_subscript(arg, cctx, &start_leader, end_leader,
!                                            bef1_tv, bef2_tv, new_tv) == FAIL
            || compile_leader(cctx, start_leader, end_leader) == FAIL)
-     {
-       clear_tv(new_tv);
        return FAIL;
-     }
      return OK;
  }
  
--- 4105,4113 ----
      // Handle following "[]", ".member", etc.
      // Then deal with prefixed '-', '+' and '!', if not done already.
      if (compile_subscript(arg, cctx, &start_leader, end_leader,
!                                                            ppconst) == FAIL
            || compile_leader(cctx, start_leader, end_leader) == FAIL)
        return FAIL;
      return OK;
  }
  
***************
*** 4093,4107 ****
   */
      static int
  compile_expr6(
!       char_u **arg,
!       cctx_T *cctx,
!       typval_T *bef_tv,
!       typval_T *new_tv)
  {
      char_u    *op;
  
      // get the first expression
!     if (compile_expr7(arg, cctx, NULL, bef_tv, new_tv) == FAIL)
        return FAIL;
  
      /*
--- 4118,4132 ----
   */
      static int
  compile_expr6(
!       char_u      **arg,
!       cctx_T      *cctx,
!       ppconst_T   *ppconst)
  {
      char_u    *op;
+     int               ppconst_used = ppconst->pp_used;
  
      // get the first expression
!     if (compile_expr7(arg, cctx, ppconst) == FAIL)
        return FAIL;
  
      /*
***************
*** 4109,4116 ****
       */
      for (;;)
      {
-       typval_T        tv2;
- 
        op = skipwhite(*arg);
        if (*op != '*' && *op != '/' && *op != '%')
            break;
--- 4134,4139 ----
***************
*** 4128,4156 ****
            return FAIL;
  
        // get the second expression
!       tv2.v_type = VAR_UNKNOWN;
!       if (compile_expr7(arg, cctx, bef_tv, new_tv, &tv2) == FAIL)
            return FAIL;
!       if (new_tv->v_type == VAR_NUMBER && tv2.v_type == VAR_NUMBER)
        {
            varnumber_T res = 0;
  
            // both are numbers: compute the result
            switch (*op)
            {
!               case '*': res = new_tv->vval.v_number * tv2.vval.v_number;
                          break;
!               case '/': res = new_tv->vval.v_number / tv2.vval.v_number;
                          break;
!               case '%': res = new_tv->vval.v_number % tv2.vval.v_number;
                          break;
            }
!           new_tv->vval.v_number = res;
        }
        else
        {
!           generate_tv_PUSH(cctx, new_tv);
!           generate_tv_PUSH(cctx, &tv2);
            generate_two_op(cctx, op);
        }
      }
--- 4151,4183 ----
            return FAIL;
  
        // get the second expression
!       if (compile_expr7(arg, cctx, ppconst) == FAIL)
            return FAIL;
! 
!       if (ppconst->pp_used == ppconst_used + 2
!               && ppconst->pp_tv[ppconst_used].v_type == VAR_NUMBER
!               && ppconst->pp_tv[ppconst_used + 1].v_type == VAR_NUMBER)
        {
+           typval_T *tv1 = &ppconst->pp_tv[ppconst_used];
+           typval_T *tv2 = &ppconst->pp_tv[ppconst_used + 1];
            varnumber_T res = 0;
  
            // both are numbers: compute the result
            switch (*op)
            {
!               case '*': res = tv1->vval.v_number * tv2->vval.v_number;
                          break;
!               case '/': res = tv1->vval.v_number / tv2->vval.v_number;
                          break;
!               case '%': res = tv1->vval.v_number % tv2->vval.v_number;
                          break;
            }
!           tv1->vval.v_number = res;
!           --ppconst->pp_used;
        }
        else
        {
!           generate_ppconst(cctx, ppconst);
            generate_two_op(cctx, op);
        }
      }
***************
*** 4166,4187 ****
      static int
  compile_expr5(char_u **arg, cctx_T *cctx)
  {
-     typval_T  tv1;
      char_u    *op;
      int               oplen;
  
      // get the first variable
!     tv1.v_type = VAR_UNKNOWN;
!     if (compile_expr6(arg, cctx, NULL, &tv1) == FAIL)
        return FAIL;
  
      /*
       * Repeat computing, until no "+", "-" or ".." is following.
       */
      for (;;)
      {
-       typval_T        tv2;
- 
        op = skipwhite(*arg);
        if (*op != '+' && *op != '-' && !(*op == '.' && (*(*arg + 1) == '.')))
            break;
--- 4193,4217 ----
      static int
  compile_expr5(char_u **arg, cctx_T *cctx)
  {
      char_u    *op;
      int               oplen;
+     ppconst_T ppconst;
+     int               ppconst_used = 0;
+ 
+     CLEAR_FIELD(ppconst);
  
      // get the first variable
!     if (compile_expr6(arg, cctx, &ppconst) == FAIL)
!     {
!       clear_ppconst(&ppconst);
        return FAIL;
+     }
  
      /*
       * Repeat computing, until no "+", "-" or ".." is following.
       */
      for (;;)
      {
        op = skipwhite(*arg);
        if (*op != '+' && *op != '-' && !(*op == '.' && (*(*arg + 1) == '.')))
            break;
***************
*** 4191,4197 ****
        {
            char_u buf[3];
  
!           clear_tv(&tv1);
            vim_strncpy(buf, op, oplen);
            semsg(_(e_white_both), buf);
            return FAIL;
--- 4221,4227 ----
        {
            char_u buf[3];
  
!           clear_ppconst(&ppconst);
            vim_strncpy(buf, op, oplen);
            semsg(_(e_white_both), buf);
            return FAIL;
***************
*** 4200,4257 ****
        *arg = skipwhite(op + oplen);
        if (may_get_next_line(op + oplen, arg, cctx) == FAIL)
        {
!           clear_tv(&tv1);
            return FAIL;
        }
  
        // get the second expression
!       tv2.v_type = VAR_UNKNOWN;
!       if (compile_expr6(arg, cctx, &tv1, &tv2) == FAIL)
        {
!           clear_tv(&tv1);
            return FAIL;
        }
  
!       if (*op == '+' && tv1.v_type == VAR_NUMBER && tv2.v_type == VAR_NUMBER)
!       {
!           // add constant numbers
!           tv1.vval.v_number = tv1.vval.v_number + tv2.vval.v_number;
!       }
!       else if (*op == '-' && tv1.v_type == VAR_NUMBER
!                                                  && tv2.v_type == VAR_NUMBER)
!       {
!           // subtract constant numbers
!           tv1.vval.v_number = tv1.vval.v_number - tv2.vval.v_number;
!       }
!       else if (*op == '.' && tv1.v_type == VAR_STRING
!                                                  && tv2.v_type == VAR_STRING)
        {
!           // concatenate constant strings
!           char_u *s1 = tv1.vval.v_string;
!           char_u *s2 = tv2.vval.v_string;
!           size_t len1 = STRLEN(s1);
  
!           tv1.vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1));
!           if (tv1.vval.v_string == NULL)
            {
                vim_free(s1);
                vim_free(s2);
-               return FAIL;
            }
!           mch_memmove(tv1.vval.v_string, s1, len1);
!           STRCPY(tv1.vval.v_string + len1, s2);
!           vim_free(s1);
!           vim_free(s2);
        }
        else
        {
!           generate_tv_PUSH(cctx, &tv1);
!           generate_tv_PUSH(cctx, &tv2);
            if (*op == '.')
            {
                if (may_generate_2STRING(-2, cctx) == FAIL
                        || may_generate_2STRING(-1, cctx) == FAIL)
                    return FAIL;
                generate_instr_drop(cctx, ISN_CONCAT, 1);
            }
            else
--- 4230,4292 ----
        *arg = skipwhite(op + oplen);
        if (may_get_next_line(op + oplen, arg, cctx) == FAIL)
        {
!           clear_ppconst(&ppconst);
            return FAIL;
        }
  
        // get the second expression
!       if (compile_expr6(arg, cctx, &ppconst) == FAIL)
        {
!           clear_ppconst(&ppconst);
            return FAIL;
        }
  
!       if (ppconst.pp_used == ppconst_used + 2
!               && (*op == '.'
!                   ? (ppconst.pp_tv[ppconst_used].v_type == VAR_STRING
!                   && ppconst.pp_tv[ppconst_used + 1].v_type == VAR_STRING)
!                   : (ppconst.pp_tv[ppconst_used].v_type == VAR_NUMBER
!                   && ppconst.pp_tv[ppconst_used + 1].v_type == VAR_NUMBER)))
        {
!           typval_T *tv1 = &ppconst.pp_tv[ppconst_used];
!           typval_T *tv2 = &ppconst.pp_tv[ppconst_used + 1];
  
!           // concat/subtract/add constant numbers
!           if (*op == '+')
!               tv1->vval.v_number = tv1->vval.v_number + tv2->vval.v_number;
!           else if (*op == '-')
!               tv1->vval.v_number = tv1->vval.v_number - tv2->vval.v_number;
!           else
            {
+               // concatenate constant strings
+               char_u *s1 = tv1->vval.v_string;
+               char_u *s2 = tv2->vval.v_string;
+               size_t len1 = STRLEN(s1);
+ 
+               tv1->vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1));
+               if (tv1->vval.v_string == NULL)
+               {
+                   clear_ppconst(&ppconst);
+                   return FAIL;
+               }
+               mch_memmove(tv1->vval.v_string, s1, len1);
+               STRCPY(tv1->vval.v_string + len1, s2);
                vim_free(s1);
                vim_free(s2);
            }
!           --ppconst.pp_used;
        }
        else
        {
!           generate_ppconst(cctx, &ppconst);
            if (*op == '.')
            {
                if (may_generate_2STRING(-2, cctx) == FAIL
                        || may_generate_2STRING(-1, cctx) == FAIL)
+               {
+                   clear_ppconst(&ppconst);
                    return FAIL;
+               }
                generate_instr_drop(cctx, ISN_CONCAT, 1);
            }
            else
***************
*** 4260,4266 ****
      }
  
      // TODO: move to caller
!     generate_tv_PUSH(cctx, &tv1);
  
      return OK;
  }
--- 4295,4301 ----
      }
  
      // TODO: move to caller
!     generate_ppconst(cctx, &ppconst);
  
      return OK;
  }
*** ../vim-8.2.0716/src/testdir/test_vim9_disassemble.vim       2020-05-07 
16:58:10.868572663 +0200
--- src/testdir/test_vim9_disassemble.vim       2020-05-08 19:05:53.883125772 
+0200
***************
*** 1039,1050 ****
--- 1039,1060 ----
    return 'one' .. 'two' .. 'three'
  enddef
  
+ def s:ComputeConst(): number
+   return 2 + 3 * 4 / 6 + 7
+ enddef
+ 
  def Test_simplify_const_expr()
    let res = execute('disass s:ConcatStrings')
    assert_match('\<SNR>\d*_ConcatStrings.*' ..
          '\d PUSHS "onetwothree".*' ..
          '\d RETURN',
          res)
+ 
+   res = execute('disass s:ComputeConst')
+   assert_match('\<SNR>\d*_ComputeConst.*' ..
+         '\d PUSHNR 11.*' ..
+         '\d RETURN',
+         res)
  enddef
  
  " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
*** ../vim-8.2.0716/src/version.c       2020-05-07 23:20:17.611439470 +0200
--- src/version.c       2020-05-08 19:09:11.006567983 +0200
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     717,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
72. Somebody at IRC just mentioned a way to obtain full motion video without
    a PC using a wireless protocol called NTSC, you wonder how you never
    heard about it

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            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/202005081711.048HBUol010364%40masaka.moolenaar.net.

Raspunde prin e-mail lui