Patch 8.2.1900
Problem:    Vim9: command modifiers do not work.
Solution:   Make most command modifiers work.
Files:      src/vim9.h, src/vim9compile.c, src/vim9execute.c,
            src/usercmd.c, src/proto/usercmd.pro, src/scriptfile.c,
            src/testdir/test_vim9_disassemble.vim


*** ../vim-8.2.1899/src/vim9.h  2020-10-23 18:02:28.707453763 +0200
--- src/vim9.h  2020-10-24 21:37:25.489883302 +0200
***************
*** 142,149 ****
  
      ISN_PUT,      // ":put", uses isn_arg.put
  
!     ISN_SILENT,           // set msg_silent or emsg_silent if arg_number is 
non-zero
!     ISN_UNSILENT,   // undo ISN_SILENT
  
      ISN_SHUFFLE,    // move item on stack up or down
      ISN_DROP      // pop stack and discard value
--- 142,149 ----
  
      ISN_PUT,      // ":put", uses isn_arg.put
  
!     ISN_CMDMOD,           // set cmdmod
!     ISN_CMDMOD_REV, // undo ISN_CMDMOD
  
      ISN_SHUFFLE,    // move item on stack up or down
      ISN_DROP      // pop stack and discard value
***************
*** 278,283 ****
--- 278,288 ----
      linenr_T  put_lnum;       // line number to put below
  } put_T;
  
+ // arguments to ISN_CMDMOD
+ typedef struct {
+     cmdmod_T  *cf_cmdmod;     // allocated
+ } cmod_T;
+ 
  /*
   * Instruction
   */
***************
*** 314,319 ****
--- 319,325 ----
        checklen_T          checklen;
        shuffle_T           shuffle;
        put_T               put;
+       cmod_T              cmdmod;
      } isn_arg;
  };
  
*** ../vim-8.2.1899/src/vim9compile.c   2020-10-24 20:49:37.502683026 +0200
--- src/vim9compile.c   2020-10-24 22:05:17.881470884 +0200
***************
*** 142,148 ****
      garray_T  ctx_type_stack;     // type of each item on the stack
      garray_T  *ctx_type_list;     // list of pointers to allocated types
  
!     int               ctx_silent;         // set when ISN_SILENT was generated
  };
  
  static void delete_def_function_contents(dfunc_T *dfunc);
--- 142,148 ----
      garray_T  ctx_type_stack;     // type of each item on the stack
      garray_T  *ctx_type_list;     // list of pointers to allocated types
  
!     int               ctx_has_cmdmod;     // ISN_CMDMOD was generated
  };
  
  static void delete_def_function_contents(dfunc_T *dfunc);
***************
*** 1823,1858 ****
  }
  
  /*
!  * Generate any instructions for side effects of "cmdmod".
   */
      static int
  generate_cmdmods(cctx_T *cctx, cmdmod_T *cmod)
  {
      isn_T     *isn;
  
!     // TODO: use more modifiers in the command
!     if (cmod->cmod_flags & (CMOD_SILENT | CMOD_ERRSILENT))
      {
!       if ((isn = generate_instr(cctx, ISN_SILENT)) == NULL)
            return FAIL;
!       isn->isn_arg.number = (cmod->cmod_flags & CMOD_ERRSILENT) != 0;
!       cctx->ctx_silent = (cmod->cmod_flags & CMOD_ERRSILENT) ? 2 : 1;
      }
      return OK;
  }
  
      static int
! generate_restore_cmdmods(cctx_T *cctx)
  {
      isn_T     *isn;
  
!     if (cctx->ctx_silent > 0)
      {
!       if ((isn = generate_instr(cctx, ISN_UNSILENT)) == NULL)
            return FAIL;
-       isn->isn_arg.number = cctx->ctx_silent == 2;
-       cctx->ctx_silent = 0;
      }
      return OK;
  }
  
--- 1823,1867 ----
  }
  
  /*
!  * Generate an instruction for any command modifiers.
   */
      static int
  generate_cmdmods(cctx_T *cctx, cmdmod_T *cmod)
  {
      isn_T     *isn;
  
!     if (cmod->cmod_flags != 0
!           || cmod->cmod_split != 0
!           || cmod->cmod_verbose != 0
!           || cmod->cmod_tab != 0
!           || cmod->cmod_filter_regmatch.regprog != NULL)
      {
!       cctx->ctx_has_cmdmod = TRUE;
! 
!       if ((isn = generate_instr(cctx, ISN_CMDMOD)) == NULL)
!           return FAIL;
!       isn->isn_arg.cmdmod.cf_cmdmod = ALLOC_ONE(cmdmod_T);
!       if (isn->isn_arg.cmdmod.cf_cmdmod == NULL)
            return FAIL;
!       mch_memmove(isn->isn_arg.cmdmod.cf_cmdmod, cmod, sizeof(cmdmod_T));
!       // filter progam now belongs to the instruction
!       cmod->cmod_filter_regmatch.regprog = NULL;
      }
+ 
      return OK;
  }
  
      static int
! generate_undo_cmdmods(cctx_T *cctx)
  {
      isn_T     *isn;
  
!     if (cctx->ctx_has_cmdmod)
      {
!       if ((isn = generate_instr(cctx, ISN_CMDMOD_REV)) == NULL)
            return FAIL;
      }
+ 
      return OK;
  }
  
***************
*** 7092,7100 ****
      for (;;)
      {
        exarg_T     ea;
-       cmdmod_T    local_cmdmod;
        int         starts_with_colon = FALSE;
        char_u      *cmd;
  
        // Bail out on the first error to avoid a flood of errors and report
        // the right line number when inside try/catch.
--- 7101,7109 ----
      for (;;)
      {
        exarg_T     ea;
        int         starts_with_colon = FALSE;
        char_u      *cmd;
+       cmdmod_T    local_cmdmod;
  
        // Bail out on the first error to avoid a flood of errors and report
        // the right line number when inside try/catch.
***************
*** 7175,7181 ****
        /*
         * COMMAND MODIFIERS
         */
!       CLEAR_FIELD(local_cmdmod);
        if (parse_command_modifiers(&ea, &errormsg, &local_cmdmod, FALSE)
                                                                       == FAIL)
        {
--- 7184,7190 ----
        /*
         * COMMAND MODIFIERS
         */
!       cctx.ctx_has_cmdmod = FALSE;
        if (parse_command_modifiers(&ea, &errormsg, &local_cmdmod, FALSE)
                                                                       == FAIL)
        {
***************
*** 7497,7503 ****
        line = skipwhite(line);
  
        // Undo any command modifiers.
!       generate_restore_cmdmods(&cctx);
  
        if (cctx.ctx_type_stack.ga_len < 0)
        {
--- 7506,7512 ----
        line = skipwhite(line);
  
        // Undo any command modifiers.
!       generate_undo_cmdmods(&cctx);
  
        if (cctx.ctx_type_stack.ga_len < 0)
        {
***************
*** 7742,7747 ****
--- 7751,7762 ----
            free_type(isn->isn_arg.type.ct_type);
            break;
  
+       case ISN_CMDMOD:
+           vim_regfree(isn->isn_arg.cmdmod.cf_cmdmod
+                                              ->cmod_filter_regmatch.regprog);
+           vim_free(isn->isn_arg.cmdmod.cf_cmdmod);
+           break;
+ 
        case ISN_2BOOL:
        case ISN_2STRING:
        case ISN_2STRING_ANY:
***************
*** 7754,7759 ****
--- 7769,7775 ----
        case ISN_CATCH:
        case ISN_CHECKLEN:
        case ISN_CHECKNR:
+       case ISN_CMDMOD_REV:
        case ISN_COMPAREANY:
        case ISN_COMPAREBLOB:
        case ISN_COMPAREBOOL:
***************
*** 7805,7811 ****
        case ISN_PUT:
        case ISN_RETURN:
        case ISN_SHUFFLE:
-       case ISN_SILENT:
        case ISN_SLICE:
        case ISN_STORE:
        case ISN_STOREDICT:
--- 7821,7826 ----
***************
*** 7819,7825 ****
        case ISN_STRSLICE:
        case ISN_THROW:
        case ISN_TRY:
-       case ISN_UNSILENT:
            // nothing allocated
            break;
      }
--- 7834,7839 ----
*** ../vim-8.2.1899/src/vim9execute.c   2020-10-23 18:02:28.711453754 +0200
--- src/vim9execute.c   2020-10-24 22:32:19.873366315 +0200
***************
*** 832,839 ****
      int               save_suppress_errthrow = suppress_errthrow;
      msglist_T **saved_msg_list = NULL;
      msglist_T *private_msg_list = NULL;
!     int               save_msg_silent = -1;
!     int               save_emsg_silent = -1;
  
  // Get pointer to item in the stack.
  #define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx)
--- 832,839 ----
      int               save_suppress_errthrow = suppress_errthrow;
      msglist_T **saved_msg_list = NULL;
      msglist_T *private_msg_list = NULL;
!     cmdmod_T  save_cmdmod;
!     int               restore_cmdmod = FALSE;
  
  // Get pointer to item in the stack.
  #define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx)
***************
*** 2816,2837 ****
                }
                break;
  
!           case ISN_SILENT:
!               if (save_msg_silent == -1)
!                   save_msg_silent = msg_silent;
!               ++msg_silent;
!               if (iptr->isn_arg.number)
!               {
!                   if (save_emsg_silent == -1)
!                       save_emsg_silent = emsg_silent;
!                   ++emsg_silent;
!               }
                break;
  
!           case ISN_UNSILENT:
!               --msg_silent;
!               if (iptr->isn_arg.number)
!                   --emsg_silent;
                break;
  
            case ISN_SHUFFLE:
--- 2816,2834 ----
                }
                break;
  
!           case ISN_CMDMOD:
!               save_cmdmod = cmdmod;
!               restore_cmdmod = TRUE;
!               cmdmod = *iptr->isn_arg.cmdmod.cf_cmdmod;
!               apply_cmdmod(&cmdmod);
                break;
  
!           case ISN_CMDMOD_REV:
!               // filter regprog is owned by the instruction, don't free it
!               cmdmod.cmod_filter_regmatch.regprog = NULL;
!               undo_cmdmod(&cmdmod);
!               cmdmod = save_cmdmod;
!               restore_cmdmod = FALSE;
                break;
  
            case ISN_SHUFFLE:
***************
*** 2905,2914 ****
      }
      msg_list = saved_msg_list;
  
!     if (save_msg_silent != -1)
!       msg_silent = save_msg_silent;
!     if (save_emsg_silent != -1)
!       emsg_silent = save_emsg_silent;
  
  failed_early:
      // Free all local variables, but not arguments.
--- 2902,2913 ----
      }
      msg_list = saved_msg_list;
  
!     if (restore_cmdmod)
!     {
!       cmdmod.cmod_filter_regmatch.regprog = NULL;
!       undo_cmdmod(&cmdmod);
!       cmdmod = save_cmdmod;
!     }
  
  failed_early:
      // Free all local variables, but not arguments.
***************
*** 3527,3536 ****
                                             (long)iptr->isn_arg.put.put_lnum);
                break;
  
!           case ISN_SILENT: smsg("%4d SILENT%s", current,
!                                      iptr->isn_arg.number ? "!" : ""); break;
!           case ISN_UNSILENT: smsg("%4d UNSILENT%s", current,
!                                      iptr->isn_arg.number ? "!" : ""); break;
  
            case ISN_SHUFFLE: smsg("%4d SHUFFLE %d up %d", current,
                                         iptr->isn_arg.shuffle.shfl_item,
--- 3526,3549 ----
                                             (long)iptr->isn_arg.put.put_lnum);
                break;
  
!               // TODO: summarize modifiers
!           case ISN_CMDMOD:
!               {
!                   char_u  *buf;
!                   int     len = produce_cmdmods(
!                                 NULL, iptr->isn_arg.cmdmod.cf_cmdmod, FALSE);
! 
!                   buf = alloc(len + 1);
!                   if (buf != NULL)
!                   {
!                       (void)produce_cmdmods(
!                                  buf, iptr->isn_arg.cmdmod.cf_cmdmod, FALSE);
!                       smsg("%4d CMDMOD %s", current, buf);
!                       vim_free(buf);
!                   }
!                   break;
!               }
!           case ISN_CMDMOD_REV: smsg("%4d CMDMOD_REV", current); break;
  
            case ISN_SHUFFLE: smsg("%4d SHUFFLE %d up %d", current,
                                         iptr->isn_arg.shuffle.shfl_item,
*** ../vim-8.2.1899/src/usercmd.c       2020-10-24 20:49:37.502683026 +0200
--- src/usercmd.c       2020-10-24 22:27:14.546124437 +0200
***************
*** 1235,1271 ****
  }
  
  /*
!  * Add modifiers from "cmdmod.cmod_split" to "buf".  Set "multi_mods" when one
   * was added.  Return the number of bytes added.
   */
      size_t
! add_win_cmd_modifers(char_u *buf, int *multi_mods)
  {
      size_t result = 0;
  
      // :aboveleft and :leftabove
!     if (cmdmod.cmod_split & WSP_ABOVE)
        result += add_cmd_modifier(buf, "aboveleft", multi_mods);
      // :belowright and :rightbelow
!     if (cmdmod.cmod_split & WSP_BELOW)
        result += add_cmd_modifier(buf, "belowright", multi_mods);
      // :botright
!     if (cmdmod.cmod_split & WSP_BOT)
        result += add_cmd_modifier(buf, "botright", multi_mods);
  
      // :tab
!     if (cmdmod.cmod_tab > 0)
        result += add_cmd_modifier(buf, "tab", multi_mods);
      // :topleft
!     if (cmdmod.cmod_split & WSP_TOP)
        result += add_cmd_modifier(buf, "topleft", multi_mods);
      // :vertical
!     if (cmdmod.cmod_split & WSP_VERT)
        result += add_cmd_modifier(buf, "vertical", multi_mods);
      return result;
  }
  
  /*
   * Check for a <> code in a user command.
   * "code" points to the '<'.  "len" the length of the <> (inclusive).
   * "buf" is where the result is to be added.
--- 1235,1338 ----
  }
  
  /*
!  * Add modifiers from "cmod->cmod_split" to "buf".  Set "multi_mods" when one
   * was added.  Return the number of bytes added.
   */
      size_t
! add_win_cmd_modifers(char_u *buf, cmdmod_T *cmod, int *multi_mods)
  {
      size_t result = 0;
  
      // :aboveleft and :leftabove
!     if (cmod->cmod_split & WSP_ABOVE)
        result += add_cmd_modifier(buf, "aboveleft", multi_mods);
      // :belowright and :rightbelow
!     if (cmod->cmod_split & WSP_BELOW)
        result += add_cmd_modifier(buf, "belowright", multi_mods);
      // :botright
!     if (cmod->cmod_split & WSP_BOT)
        result += add_cmd_modifier(buf, "botright", multi_mods);
  
      // :tab
!     if (cmod->cmod_tab > 0)
        result += add_cmd_modifier(buf, "tab", multi_mods);
      // :topleft
!     if (cmod->cmod_split & WSP_TOP)
        result += add_cmd_modifier(buf, "topleft", multi_mods);
      // :vertical
!     if (cmod->cmod_split & WSP_VERT)
        result += add_cmd_modifier(buf, "vertical", multi_mods);
      return result;
  }
  
  /*
+  * Generate text for the "cmod" command modifiers.
+  * If "buf" is NULL just return the length.
+  */
+     int
+ produce_cmdmods(char_u *buf, cmdmod_T *cmod, int quote)
+ {
+     int           result = 0;
+     int           multi_mods = 0;
+     int           i;
+     typedef struct {
+       int flag;
+       char *name;
+     } mod_entry_T;
+     static mod_entry_T mod_entries[] = {
+ #ifdef FEAT_BROWSE_CMD
+       {CMOD_BROWSE, "browse"},
+ #endif
+ #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
+       {CMOD_CONFIRM, "confirm"},
+ #endif
+       {CMOD_HIDE, "hide"},
+       {CMOD_KEEPALT, "keepalt"},
+       {CMOD_KEEPJUMPS, "keepjumps"},
+       {CMOD_KEEPMARKS, "keepmarks"},
+       {CMOD_KEEPPATTERNS, "keeppatterns"},
+       {CMOD_LOCKMARKS, "lockmarks"},
+       {CMOD_NOSWAPFILE, "noswapfile"},
+       {CMOD_UNSILENT, "unsilent"},
+       {CMOD_NOAUTOCMD, "noautocmd"},
+ #ifdef HAVE_SANDBOX
+       {CMOD_SANDBOX, "sandbox"},
+ #endif
+       {0, NULL}
+     };
+ 
+     result = quote ? 2 : 0;
+     if (buf != NULL)
+     {
+       if (quote)
+           *buf++ = '"';
+       *buf = '\0';
+     }
+ 
+     // the modifiers that are simple flags
+     for (i = 0; mod_entries[i].name != NULL; ++i)
+       if (cmod->cmod_flags & mod_entries[i].flag)
+           result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods);
+ 
+     // :silent
+     if (cmod->cmod_flags & CMOD_SILENT)
+       result += add_cmd_modifier(buf,
+                       (cmod->cmod_flags & CMOD_ERRSILENT) ? "silent!"
+                                                     : "silent", &multi_mods);
+     // :verbose
+     if (p_verbose > 0)
+       result += add_cmd_modifier(buf, "verbose", &multi_mods);
+     // flags from cmod->cmod_split
+     result += add_win_cmd_modifers(buf, cmod, &multi_mods);
+     if (quote && buf != NULL)
+     {
+       buf += result - 2;
+       *buf = '"';
+     }
+     return result;
+ }
+ 
+ /*
   * Check for a <> code in a user command.
   * "code" points to the '<'.  "len" the length of the <> (inclusive).
   * "buf" is where the result is to be added.
***************
*** 1452,1513 ****
  
      case ct_MODS:
      {
!       int multi_mods = 0;
!       typedef struct {
!           int flag;
!           char *name;
!       } mod_entry_T;
!       static mod_entry_T mod_entries[] = {
! #ifdef FEAT_BROWSE_CMD
!           {CMOD_BROWSE, "browse"},
! #endif
! #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
!           {CMOD_CONFIRM, "confirm"},
! #endif
!           {CMOD_HIDE, "hide"},
!           {CMOD_KEEPALT, "keepalt"},
!           {CMOD_KEEPJUMPS, "keepjumps"},
!           {CMOD_KEEPMARKS, "keepmarks"},
!           {CMOD_KEEPPATTERNS, "keeppatterns"},
!           {CMOD_LOCKMARKS, "lockmarks"},
!           {CMOD_NOSWAPFILE, "noswapfile"},
!           {0, NULL}
!       };
!       int i;
! 
!       result = quote ? 2 : 0;
!       if (buf != NULL)
!       {
!           if (quote)
!               *buf++ = '"';
!           *buf = '\0';
!       }
! 
!       // the modifiers that are simple flags
!       for (i = 0; mod_entries[i].name != NULL; ++i)
!           if (cmdmod.cmod_flags & mod_entries[i].flag)
!               result += add_cmd_modifier(buf, mod_entries[i].name,
!                                                                &multi_mods);
! 
!       // TODO: How to support :noautocmd?
! #ifdef HAVE_SANDBOX
!       // TODO: How to support :sandbox?
! #endif
!       // :silent
!       if (msg_silent > 0)
!           result += add_cmd_modifier(buf,
!                   emsg_silent > 0 ? "silent!" : "silent", &multi_mods);
!       // TODO: How to support :unsilent?
!       // :verbose
!       if (p_verbose > 0)
!           result += add_cmd_modifier(buf, "verbose", &multi_mods);
!       // flags from cmdmod.cmod_split
!       result += add_win_cmd_modifers(buf, &multi_mods);
!       if (quote && buf != NULL)
!       {
!           buf += result - 2;
!           *buf = '"';
!       }
        break;
      }
  
--- 1519,1525 ----
  
      case ct_MODS:
      {
!       result = produce_cmdmods(buf, &cmdmod, quote);
        break;
      }
  
*** ../vim-8.2.1899/src/proto/usercmd.pro       2020-04-13 21:16:18.039292270 
+0200
--- src/proto/usercmd.pro       2020-10-24 22:27:17.942115967 +0200
***************
*** 14,19 ****
  void ex_comclear(exarg_T *eap);
  void uc_clear(garray_T *gap);
  void ex_delcommand(exarg_T *eap);
! size_t add_win_cmd_modifers(char_u *buf, int *multi_mods);
  void do_ucmd(exarg_T *eap);
  /* vim: set ft=c : */
--- 14,20 ----
  void ex_comclear(exarg_T *eap);
  void uc_clear(garray_T *gap);
  void ex_delcommand(exarg_T *eap);
! size_t add_win_cmd_modifers(char_u *buf, cmdmod_T *cmod, int *multi_mods);
! int produce_cmdmods(char_u *buf, cmdmod_T *cmod, int quote);
  void do_ucmd(exarg_T *eap);
  /* vim: set ft=c : */
*** ../vim-8.2.1899/src/scriptfile.c    2020-10-24 20:49:37.502683026 +0200
--- src/scriptfile.c    2020-10-24 22:25:13.686426083 +0200
***************
*** 1009,1015 ****
      int           multi_mods = 0;
  
      buf[0] = NUL;
!     (void)add_win_cmd_modifers(buf, &multi_mods);
  
      vim_setenv((char_u *)"OPTWIN_CMD", buf);
      cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
--- 1009,1015 ----
      int           multi_mods = 0;
  
      buf[0] = NUL;
!     (void)add_win_cmd_modifers(buf, &cmdmod, &multi_mods);
  
      vim_setenv((char_u *)"OPTWIN_CMD", buf);
      cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
*** ../vim-8.2.1899/src/testdir/test_vim9_disassemble.vim       2020-10-23 
18:02:28.711453754 +0200
--- src/testdir/test_vim9_disassemble.vim       2020-10-24 22:33:12.893235083 
+0200
***************
*** 1627,1641 ****
    var res = execute('disass s:SilentMessage')
    assert_match('<SNR>\d*_SilentMessage\_s*' ..
          'silent echomsg "text"\_s*' ..
!         '\d SILENT\_s*' ..
          '\d PUSHS "text"\_s*' ..
          '\d ECHOMSG 1\_s*' ..
!         '\d UNSILENT\_s*' ..
          'silent! echoerr "error"\_s*' ..
!         '\d SILENT!\_s*' ..
          '\d PUSHS "error"\_s*' ..
          '\d ECHOERR 1\_s*' ..
!         '\d UNSILENT!\_s*' ..
          '\d PUSHNR 0\_s*' ..
          '\d RETURN',
          res)
--- 1627,1641 ----
    var res = execute('disass s:SilentMessage')
    assert_match('<SNR>\d*_SilentMessage\_s*' ..
          'silent echomsg "text"\_s*' ..
!         '\d CMDMOD silent\_s*' ..
          '\d PUSHS "text"\_s*' ..
          '\d ECHOMSG 1\_s*' ..
!         '\d CMDMOD_REV\_s*' ..
          'silent! echoerr "error"\_s*' ..
!         '\d CMDMOD silent!\_s*' ..
          '\d PUSHS "error"\_s*' ..
          '\d ECHOERR 1\_s*' ..
!         '\d CMDMOD_REV\_s*' ..
          '\d PUSHNR 0\_s*' ..
          '\d RETURN',
          res)
*** ../vim-8.2.1899/src/version.c       2020-10-24 20:58:02.533121768 +0200
--- src/version.c       2020-10-24 23:06:40.563791658 +0200
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     1900,
  /**/

-- 
I have a watch cat! Just break in and she'll watch.

 /// 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/202010242109.09OL9S7q1136009%40masaka.moolenaar.net.

Raspunde prin e-mail lui