changeset 22703:f2bfee4ac356 v8.2.1900

patch 8.2.1900: Vim9: command modifiers do not work Commit: https://github.com/vim/vim/commit/02194d2bd54eacd0b7b9a017a3fe1702ecb80971 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Oct 24 23:08:38 2020 +0200 patch 8.2.1900: Vim9: command modifiers do not work Problem: Vim9: command modifiers do not work. Solution: Make most command modifiers work.
author Bram Moolenaar <Bram@vim.org>
date Sat, 24 Oct 2020 23:15:04 +0200
parents 81d7d3860786
children 8fef39a36cb9
files src/proto/usercmd.pro src/scriptfile.c src/testdir/test_vim9_disassemble.vim src/usercmd.c src/version.c src/vim9.h src/vim9compile.c src/vim9execute.c
diffstat 8 files changed, 165 insertions(+), 117 deletions(-) [+]
line wrap: on
line diff
--- a/src/proto/usercmd.pro
+++ b/src/proto/usercmd.pro
@@ -14,6 +14,7 @@ void ex_command(exarg_T *eap);
 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);
+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 : */
--- a/src/scriptfile.c
+++ b/src/scriptfile.c
@@ -1009,7 +1009,7 @@ ex_options(
     int	    multi_mods = 0;
 
     buf[0] = NUL;
-    (void)add_win_cmd_modifers(buf, &multi_mods);
+    (void)add_win_cmd_modifers(buf, &cmdmod, &multi_mods);
 
     vim_setenv((char_u *)"OPTWIN_CMD", buf);
     cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -1627,15 +1627,15 @@ def Test_silent()
   var res = execute('disass s:SilentMessage')
   assert_match('<SNR>\d*_SilentMessage\_s*' ..
         'silent echomsg "text"\_s*' ..
-        '\d SILENT\_s*' ..
+        '\d CMDMOD silent\_s*' ..
         '\d PUSHS "text"\_s*' ..
         '\d ECHOMSG 1\_s*' ..
-        '\d UNSILENT\_s*' ..
+        '\d CMDMOD_REV\_s*' ..
         'silent! echoerr "error"\_s*' ..
-        '\d SILENT!\_s*' ..
+        '\d CMDMOD silent!\_s*' ..
         '\d PUSHS "error"\_s*' ..
         '\d ECHOERR 1\_s*' ..
-        '\d UNSILENT!\_s*' ..
+        '\d CMDMOD_REV\_s*' ..
         '\d PUSHNR 0\_s*' ..
         '\d RETURN',
         res)
--- a/src/usercmd.c
+++ b/src/usercmd.c
@@ -1235,37 +1235,104 @@ add_cmd_modifier(char_u *buf, char *mod_
 }
 
 /*
- * Add modifiers from "cmdmod.cmod_split" to "buf".  Set "multi_mods" when one
+ * 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, int *multi_mods)
+add_win_cmd_modifers(char_u *buf, cmdmod_T *cmod, int *multi_mods)
 {
     size_t result = 0;
 
     // :aboveleft and :leftabove
-    if (cmdmod.cmod_split & WSP_ABOVE)
+    if (cmod->cmod_split & WSP_ABOVE)
 	result += add_cmd_modifier(buf, "aboveleft", multi_mods);
     // :belowright and :rightbelow
-    if (cmdmod.cmod_split & WSP_BELOW)
+    if (cmod->cmod_split & WSP_BELOW)
 	result += add_cmd_modifier(buf, "belowright", multi_mods);
     // :botright
-    if (cmdmod.cmod_split & WSP_BOT)
+    if (cmod->cmod_split & WSP_BOT)
 	result += add_cmd_modifier(buf, "botright", multi_mods);
 
     // :tab
-    if (cmdmod.cmod_tab > 0)
+    if (cmod->cmod_tab > 0)
 	result += add_cmd_modifier(buf, "tab", multi_mods);
     // :topleft
-    if (cmdmod.cmod_split & WSP_TOP)
+    if (cmod->cmod_split & WSP_TOP)
 	result += add_cmd_modifier(buf, "topleft", multi_mods);
     // :vertical
-    if (cmdmod.cmod_split & WSP_VERT)
+    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,62 +1519,7 @@ uc_check_code(
 
     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 = '"';
-	}
+	result = produce_cmdmods(buf, &cmdmod, quote);
 	break;
     }
 
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1900,
+/**/
     1899,
 /**/
     1898,
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -142,8 +142,8 @@ typedef enum {
 
     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_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,6 +278,11 @@ typedef struct {
     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,6 +319,7 @@ struct isn_S {
 	checklen_T	    checklen;
 	shuffle_T	    shuffle;
 	put_T		    put;
+	cmod_T		    cmdmod;
     } isn_arg;
 };
 
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -142,7 +142,7 @@ struct cctx_S {
     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
+    int		ctx_has_cmdmod;	    // ISN_CMDMOD was generated
 };
 
 static void delete_def_function_contents(dfunc_T *dfunc);
@@ -1823,36 +1823,45 @@ generate_EXECCONCAT(cctx_T *cctx, int co
 }
 
 /*
- * Generate any instructions for side effects of "cmdmod".
+ * Generate an instruction for any command modifiers.
  */
     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)
+    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.number = (cmod->cmod_flags & CMOD_ERRSILENT) != 0;
-	cctx->ctx_silent = (cmod->cmod_flags & CMOD_ERRSILENT) ? 2 : 1;
-    }
+	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_restore_cmdmods(cctx_T *cctx)
+generate_undo_cmdmods(cctx_T *cctx)
 {
     isn_T	*isn;
 
-    if (cctx->ctx_silent > 0)
-    {
-	if ((isn = generate_instr(cctx, ISN_UNSILENT)) == NULL)
+    if (cctx->ctx_has_cmdmod)
+    {
+	if ((isn = generate_instr(cctx, ISN_CMDMOD_REV)) == NULL)
 	    return FAIL;
-	isn->isn_arg.number = cctx->ctx_silent == 2;
-	cctx->ctx_silent = 0;
-    }
+    }
+
     return OK;
 }
 
@@ -7092,9 +7101,9 @@ compile_def_function(ufunc_T *ufunc, int
     for (;;)
     {
 	exarg_T	    ea;
-	cmdmod_T    local_cmdmod;
 	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,7 +7184,7 @@ compile_def_function(ufunc_T *ufunc, int
 	/*
 	 * COMMAND MODIFIERS
 	 */
-	CLEAR_FIELD(local_cmdmod);
+	cctx.ctx_has_cmdmod = FALSE;
 	if (parse_command_modifiers(&ea, &errormsg, &local_cmdmod, FALSE)
 								       == FAIL)
 	{
@@ -7497,7 +7506,7 @@ nextline:
 	line = skipwhite(line);
 
 	// Undo any command modifiers.
-	generate_restore_cmdmods(&cctx);
+	generate_undo_cmdmods(&cctx);
 
 	if (cctx.ctx_type_stack.ga_len < 0)
 	{
@@ -7742,6 +7751,12 @@ delete_instr(isn_T *isn)
 	    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,6 +7769,7 @@ delete_instr(isn_T *isn)
 	case ISN_CATCH:
 	case ISN_CHECKLEN:
 	case ISN_CHECKNR:
+	case ISN_CMDMOD_REV:
 	case ISN_COMPAREANY:
 	case ISN_COMPAREBLOB:
 	case ISN_COMPAREBOOL:
@@ -7805,7 +7821,6 @@ delete_instr(isn_T *isn)
 	case ISN_PUT:
 	case ISN_RETURN:
 	case ISN_SHUFFLE:
-	case ISN_SILENT:
 	case ISN_SLICE:
 	case ISN_STORE:
 	case ISN_STOREDICT:
@@ -7819,7 +7834,6 @@ delete_instr(isn_T *isn)
 	case ISN_STRSLICE:
 	case ISN_THROW:
 	case ISN_TRY:
-	case ISN_UNSILENT:
 	    // nothing allocated
 	    break;
     }
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -832,8 +832,8 @@ call_def_function(
     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;
+    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,22 +2816,19 @@ call_def_function(
 		}
 		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;
-		}
+	    case ISN_CMDMOD:
+		save_cmdmod = cmdmod;
+		restore_cmdmod = TRUE;
+		cmdmod = *iptr->isn_arg.cmdmod.cf_cmdmod;
+		apply_cmdmod(&cmdmod);
 		break;
 
-	    case ISN_UNSILENT:
-		--msg_silent;
-		if (iptr->isn_arg.number)
-		    --emsg_silent;
+	    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,10 +2902,12 @@ failed:
     }
     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;
+    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,10 +3526,24 @@ ex_disassemble(exarg_T *eap)
 					     (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;
+		// 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,