changeset 22697:c996700d569f v8.2.1897

patch 8.2.1897: command modifiers are saved and set inconsistently Commit: https://github.com/vim/vim/commit/5661ed6c833e05467cab33cb9b1c535e7e5cc570 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Oct 24 17:19:16 2020 +0200 patch 8.2.1897: command modifiers are saved and set inconsistently Problem: Command modifiers are saved and set inconsistently. Solution: Separate parsing and applying command modifiers. Save values in cmdmod_T.
author Bram Moolenaar <Bram@vim.org>
date Sat, 24 Oct 2020 17:30:04 +0200
parents 919563736398
children 28523bc9ee71
files src/ex_cmds.h src/ex_docmd.c src/proto/ex_docmd.pro src/structs.h src/version.c src/vim9compile.c
diffstat 6 files changed, 118 insertions(+), 85 deletions(-) [+]
line wrap: on
line diff
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -1884,12 +1884,6 @@ struct exarg
 #ifdef FEAT_EVAL
     cstack_T	*cstack;	// condition stack for ":if" etc.
 #endif
-    long	verbose_save;	 // saved value of p_verbose
-    int		save_msg_silent; // saved value of msg_silent
-    int		did_esilent;	 // how many times emsg_silent was incremented
-#ifdef HAVE_SANDBOX
-    int		did_sandbox;	// when TRUE did ++sandbox
-#endif
 };
 
 #define FORCE_BIN 1		// ":edit ++bin file"
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -1764,6 +1764,7 @@ do_one_cmd(
 #endif
     if (parse_command_modifiers(&ea, &errormsg, FALSE) == FAIL)
 	goto doend;
+    apply_cmdmod(&cmdmod);
 
     after_modifier = ea.cmd;
 
@@ -2515,12 +2516,12 @@ do_one_cmd(
 
     // The :try command saves the emsg_silent flag, reset it here when
     // ":silent! try" was used, it should only apply to :try itself.
-    if (ea.cmdidx == CMD_try && ea.did_esilent > 0)
-    {
-	emsg_silent -= ea.did_esilent;
+    if (ea.cmdidx == CMD_try && cmdmod.cmod_did_esilent > 0)
+    {
+	emsg_silent -= cmdmod.cmod_did_esilent;
 	if (emsg_silent < 0)
 	    emsg_silent = 0;
-	ea.did_esilent = 0;
+	cmdmod.cmod_did_esilent = 0;
     }
 
 /*
@@ -2597,15 +2598,10 @@ doend:
 			? cmdnames[(int)ea.cmdidx].cmd_name : (char_u *)NULL);
 #endif
 
-    undo_cmdmod(&ea, save_msg_scroll);
+    undo_cmdmod(save_msg_scroll);
     cmdmod = save_cmdmod;
     reg_executing = save_reg_executing;
 
-#ifdef HAVE_SANDBOX
-    if (ea.did_sandbox)
-	--sandbox;
-#endif
-
     if (ea.nextcmd && *ea.nextcmd == NUL)	// not really a next command
 	ea.nextcmd = NULL;
 
@@ -2641,10 +2637,11 @@ ex_errmsg(char *msg, char_u *arg)
  * - Set ex_pressedreturn for an empty command line.
  * - set msg_silent for ":silent"
  * - set 'eventignore' to "all" for ":noautocmd"
- * - set p_verbose for ":verbose"
- * - Increment "sandbox" for ":sandbox"
  * When "skip_only" is TRUE the global variables are not changed, except for
  * "cmdmod".
+ * Call apply_cmdmod() to get the side effects of the modifiers:
+ * - Increment "sandbox" for ":sandbox"
+ * - set p_verbose for ":verbose"
  * Return FAIL when the command is not to be executed.
  * May set "errormsg" to an error message.
  */
@@ -2655,8 +2652,6 @@ parse_command_modifiers(exarg_T *eap, ch
     int	    starts_with_colon = FALSE;
 
     CLEAR_FIELD(cmdmod);
-    eap->verbose_save = -1;
-    eap->save_msg_silent = -1;
 
     // Repeat until no more command modifiers are found.
     for (;;)
@@ -2800,14 +2795,7 @@ parse_command_modifiers(exarg_T *eap, ch
 
 	    case 'n':	if (checkforcmd(&eap->cmd, "noautocmd", 3))
 			{
-			    if (cmdmod.save_ei == NULL && !skip_only)
-			    {
-				// Set 'eventignore' to "all". Restore the
-				// existing option value later.
-				cmdmod.save_ei = vim_strsave(p_ei);
-				set_string_option_direct((char_u *)"ei", -1,
-					 (char_u *)"all", OPT_FREE, SID_NONE);
-			    }
+			    cmdmod.cmod_flags |= CMOD_NOAUTOCMD;
 			    continue;
 			}
 			if (!checkforcmd(&eap->cmd, "noswapfile", 3))
@@ -2822,37 +2810,18 @@ parse_command_modifiers(exarg_T *eap, ch
 
 	    case 's':	if (checkforcmd(&eap->cmd, "sandbox", 3))
 			{
-#ifdef HAVE_SANDBOX
-			    if (!skip_only)
-			    {
-				if (!eap->did_sandbox)
-				    ++sandbox;
-				eap->did_sandbox = TRUE;
-			    }
-#endif
+			    cmdmod.cmod_flags |= CMOD_SANDBOX;
 			    continue;
 			}
 			if (!checkforcmd(&eap->cmd, "silent", 3))
 			    break;
-			if (!skip_only)
-			{
-			    if (eap->save_msg_silent == -1)
-				eap->save_msg_silent = msg_silent;
-			    ++msg_silent;
-			}
+			cmdmod.cmod_flags |= CMOD_SILENT;
 			if (*eap->cmd == '!' && !VIM_ISWHITE(eap->cmd[-1]))
 			{
 			    // ":silent!", but not "silent !cmd"
 			    eap->cmd = skipwhite(eap->cmd + 1);
-			    if (!skip_only)
-			    {
-				++emsg_silent;
-				++eap->did_esilent;
-			    }
-			    cmdmod.emsg_silent = TRUE;
+			    cmdmod.cmod_flags |= CMOD_ERRSILENT;
 			}
-			else
-			    cmdmod.msg_silent = TRUE;
 			continue;
 
 	    case 't':	if (checkforcmd(&p, "tab", 3))
@@ -2884,12 +2853,7 @@ parse_command_modifiers(exarg_T *eap, ch
 
 	    case 'u':	if (!checkforcmd(&eap->cmd, "unsilent", 3))
 			    break;
-			if (!skip_only)
-			{
-			    if (eap->save_msg_silent == -1)
-				eap->save_msg_silent = msg_silent;
-			    msg_silent = 0;
-			}
+			cmdmod.cmod_flags |= CMOD_UNSILENT;
 			continue;
 
 	    case 'v':	if (checkforcmd(&eap->cmd, "vertical", 4))
@@ -2899,15 +2863,10 @@ parse_command_modifiers(exarg_T *eap, ch
 			}
 			if (!checkforcmd(&p, "verbose", 4))
 			    break;
-			if (!skip_only)
-			{
-			    if (eap->verbose_save < 0)
-				eap->verbose_save = p_verbose;
-			    if (vim_isdigit(*eap->cmd))
-				p_verbose = atoi((char *)eap->cmd);
-			    else
-				p_verbose = 1;
-			}
+			if (vim_isdigit(*eap->cmd))
+			    cmdmod.cmod_verbose = atoi((char *)eap->cmd);
+			else
+			    cmdmod.cmod_verbose = 1;
 			eap->cmd = p;
 			continue;
 	}
@@ -2918,32 +2877,89 @@ parse_command_modifiers(exarg_T *eap, ch
 }
 
 /*
+ * Apply the command modifiers.  Saves current state in "cmdmod", call
+ * undo_cmdmod() later.
+ */
+    void
+apply_cmdmod(cmdmod_T *cmod)
+{
+#ifdef HAVE_SANDBOX
+    if ((cmod->cmod_flags & CMOD_SANDBOX) && !cmod->cmod_did_sandbox)
+    {
+	++sandbox;
+	cmod->cmod_did_sandbox = TRUE;
+    }
+#endif
+    if (cmod->cmod_verbose > 0)
+    {
+	if (cmod->cmod_verbose_save == 0)
+	    cmod->cmod_verbose_save = p_verbose + 1;
+	p_verbose = cmod->cmod_verbose;
+    }
+
+    if ((cmod->cmod_flags & (CMOD_SILENT | CMOD_UNSILENT))
+	    && cmod->cmod_save_msg_silent == 0)
+	cmod->cmod_save_msg_silent = msg_silent + 1;
+    if (cmod->cmod_flags & CMOD_SILENT)
+	++msg_silent;
+    if (cmod->cmod_flags & CMOD_UNSILENT)
+	msg_silent = 0;
+
+    if (cmod->cmod_flags & CMOD_ERRSILENT)
+    {
+	++emsg_silent;
+	++cmod->cmod_did_esilent;
+    }
+
+    if ((cmod->cmod_flags & CMOD_NOAUTOCMD) && cmdmod.cmod_save_ei == NULL)
+    {
+	// Set 'eventignore' to "all".
+	// First save the existing option value for restoring it later.
+	cmdmod.cmod_save_ei = vim_strsave(p_ei);
+	set_string_option_direct((char_u *)"ei", -1,
+					  (char_u *)"all", OPT_FREE, SID_NONE);
+    }
+}
+
+/*
  * Undo and free contents of "cmdmod".
  */
     void
-undo_cmdmod(exarg_T *eap, int save_msg_scroll)
-{
-    if (eap->verbose_save >= 0)
-	p_verbose = eap->verbose_save;
-
-    if (cmdmod.save_ei != NULL)
+undo_cmdmod(int save_msg_scroll)
+{
+    if (cmdmod.cmod_verbose_save > 0)
+    {
+	p_verbose = cmdmod.cmod_verbose_save - 1;
+	cmdmod.cmod_verbose_save = 0;
+    }
+
+#ifdef HAVE_SANDBOX
+    if (cmdmod.cmod_did_sandbox)
+    {
+	--sandbox;
+	cmdmod.cmod_did_sandbox = FALSE;
+    }
+#endif
+
+    if (cmdmod.cmod_save_ei != NULL)
     {
 	// Restore 'eventignore' to the value before ":noautocmd".
-	set_string_option_direct((char_u *)"ei", -1, cmdmod.save_ei,
+	set_string_option_direct((char_u *)"ei", -1, cmdmod.cmod_save_ei,
 							  OPT_FREE, SID_NONE);
-	free_string_option(cmdmod.save_ei);
+	free_string_option(cmdmod.cmod_save_ei);
+	cmdmod.cmod_save_ei = NULL;
     }
 
     if (cmdmod.filter_regmatch.regprog != NULL)
 	vim_regfree(cmdmod.filter_regmatch.regprog);
 
-    if (eap->save_msg_silent != -1)
+    if (cmdmod.cmod_save_msg_silent > 0)
     {
 	// messages could be enabled for a serious error, need to check if the
 	// counters don't become negative
-	if (!did_emsg || msg_silent > eap->save_msg_silent)
-	    msg_silent = eap->save_msg_silent;
-	emsg_silent -= eap->did_esilent;
+	if (!did_emsg || msg_silent > cmdmod.cmod_save_msg_silent - 1)
+	    msg_silent = cmdmod.cmod_save_msg_silent - 1;
+	emsg_silent -= cmdmod.cmod_did_esilent;
 	if (emsg_silent < 0)
 	    emsg_silent = 0;
 	// Restore msg_scroll, it's set by file I/O commands, even when no
@@ -2954,6 +2970,9 @@ undo_cmdmod(exarg_T *eap, int save_msg_s
 	// somewhere in the line.  Put it back in the first column.
 	if (redirecting())
 	    msg_col = 0;
+
+	cmdmod.cmod_save_msg_silent = 0;
+	cmdmod.cmod_did_esilent = 0;
     }
 }
 
--- a/src/proto/ex_docmd.pro
+++ b/src/proto/ex_docmd.pro
@@ -7,7 +7,8 @@ void *getline_cookie(char_u *(*fgetline)
 char_u *getline_peek(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie);
 char *ex_errmsg(char *msg, char_u *arg);
 int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only);
-void undo_cmdmod(exarg_T *eap, int save_msg_scroll);
+void apply_cmdmod(cmdmod_T *cmod);
+void undo_cmdmod(int save_msg_scroll);
 int parse_cmd_address(exarg_T *eap, char **errormsg, int silent);
 int checkforcmd(char_u **pp, char *cmd, int len);
 char_u *skip_option_env_lead(char_u *start);
--- a/src/structs.h
+++ b/src/structs.h
@@ -625,6 +625,7 @@ typedef struct
  */
 typedef struct
 {
+    int		cmod_flags;		// CMOD_ flags, see below
     int		hide;			// TRUE when ":hide" was used
 # ifdef FEAT_BROWSE_CMD
     int		browse;			// TRUE to invoke file dialog
@@ -640,13 +641,29 @@ typedef struct
     int		lockmarks;		// TRUE when ":lockmarks" was used
     int		keeppatterns;		// TRUE when ":keeppatterns" was used
     int		noswapfile;		// TRUE when ":noswapfile" was used
-    char_u	*save_ei;		// saved value of 'eventignore'
     regmatch_T	filter_regmatch;	// set by :filter /pat/
     int		filter_force;		// set for :filter!
-    int		msg_silent;		// TRUE when ":silent" was used
-    int		emsg_silent;		// TRUE when ":silent!" was used
+
+    int		cmod_verbose;		// non-zero to set 'verbose'
+
+    // values for undo_cmdmod()
+    char_u	*cmod_save_ei;		// saved value of 'eventignore'
+#ifdef HAVE_SANDBOX
+    int		cmod_did_sandbox;	// set when "sandbox" was incremented
+#endif
+    long	cmod_verbose_save;	// if 'verbose' was set: value of
+					// p_verbose plus one
+    int		cmod_save_msg_silent;	// if non-zero: saved value of
+					// msg_silent + 1
+    int		cmod_did_esilent;	// incremented when emsg_silent is
 } cmdmod_T;
 
+#define CMOD_SANDBOX	0x01
+#define CMOD_SILENT	0x02
+#define CMOD_ERRSILENT	0x04
+#define CMOD_UNSILENT	0x08
+#define CMOD_NOAUTOCMD	0x10
+
 #define MF_SEED_LEN	8
 
 struct memfile
--- 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 */
 /**/
+    1897,
+/**/
     1896,
 /**/
     1895,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -1831,12 +1831,12 @@ generate_cmdmods(cctx_T *cctx)
     isn_T	*isn;
 
     // TODO: use more modifiers in the command
-    if (cmdmod.msg_silent || cmdmod.emsg_silent)
+    if (cmdmod.cmod_flags & (CMOD_SILENT | CMOD_ERRSILENT))
     {
 	if ((isn = generate_instr(cctx, ISN_SILENT)) == NULL)
 	    return FAIL;
-	isn->isn_arg.number = cmdmod.emsg_silent;
-	cctx->ctx_silent = cmdmod.emsg_silent ? 2 : 1;
+	isn->isn_arg.number = (cmdmod.cmod_flags & CMOD_ERRSILENT) != 0;
+	cctx->ctx_silent = (cmdmod.cmod_flags & CMOD_ERRSILENT) ? 2 : 1;
     }
     return OK;
 }
@@ -7187,7 +7187,7 @@ compile_def_function(ufunc_T *ufunc, int
 	}
 	generate_cmdmods(&cctx);
 
-	undo_cmdmod(&ea, save_msg_scroll);
+	undo_cmdmod(save_msg_scroll);
 	cmdmod = save_cmdmod;
 
 	// Skip ":call" to get to the function name.