changeset 25939:377a7686a52f v8.2.3503

patch 8.2.3503: Vim9: using g:pat:cmd is confusing Commit: https://github.com/vim/vim/commit/7b829268921e8fc1c63c34d245063c1c4e7d21af Author: Bram Moolenaar <Bram@vim.org> Date: Wed Oct 13 15:04:34 2021 +0100 patch 8.2.3503: Vim9: using g:pat:cmd is confusing Problem: Vim9: using g:pat:cmd is confusing. Solution: Do not recognize g: as the :global command. Also for s:pat:repl. (closes #8982)
author Bram Moolenaar <Bram@vim.org>
date Wed, 13 Oct 2021 16:15:04 +0200
parents 26aa27d37199
children cc0ec4ebcae8
files runtime/doc/vim9.txt src/errors.h src/ex_cmds.c src/ex_docmd.c src/proto/vim9compile.pro src/testdir/test_vim9_cmd.vim src/version.c src/vim9compile.c
diffstat 8 files changed, 111 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/vim9.txt
+++ b/runtime/doc/vim9.txt
@@ -942,8 +942,21 @@ Ex command ranges need to be prefixed wi
 
 Some Ex commands can be confused with assignments in Vim9 script: >
 	g:name = value    # assignment
+	:g:pattern:cmd	  # :global command
+
+To avoid confusion between a `:global` or `:substitute` command and an
+expression or assignment, a few separators cannot be used when these commands
+are abbreviated to a single character: ':', '-' and '.'. >
 	g:pattern:cmd	  # invalid command - ERROR
-	:g:pattern:cmd	  # :global command
+	s:pattern:repl	  # invalid command - ERROR
+	g-pattern-cmd	  # invalid command - ERROR
+	s-pattern-repl	  # invalid command - ERROR
+	g.pattern.cmd	  # invalid command - ERROR
+	s.pattern.repl	  # invalid command - ERROR
+
+Also, there cannot be a space between the command and the separator: >
+	g /pattern/cmd	  # invalid command - ERROR
+	s /pattern/repl	  # invalid command - ERROR
 
 Functions defined with `:def` compile the whole function.  Legacy functions
 can bail out, and the following lines are not parsed: >
--- a/src/errors.h
+++ b/src/errors.h
@@ -666,3 +666,7 @@ EXTERN char e_invalid_value_for_blob_nr[
 	INIT(= N_("E1239: Invalid value for blob: %d"));
 EXTERN char e_resulting_text_too_long[]
 	INIT(= N_("E1240: Resulting text too long"));
+EXTERN char e_separator_not_supported_str[]
+	INIT(= N_("E1241: Separator not supported: %s"));
+EXTERN char e_no_white_space_allowed_before_separator_str[]
+	INIT(= N_("E1242: No white space allowed before separator: %s"));
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -3724,6 +3724,9 @@ ex_substitute(exarg_T *eap)
 				// don't accept alphanumeric for separator
 	if (check_regexp_delim(*cmd) == FAIL)
 	    return;
+	if (in_vim9script() && check_global_and_subst(eap->cmd, eap->arg)
+								      == FAIL)
+	    return;
 
 	/*
 	 * undocumented vi feature:
@@ -4899,6 +4902,9 @@ ex_global(exarg_T *eap)
     cmd = eap->arg;
     which_pat = RE_LAST;	    // default: use last used regexp
 
+    if (in_vim9script() && check_global_and_subst(eap->cmd, eap->arg) == FAIL)
+	return;
+
     /*
      * undocumented vi feature:
      *	"\/" and "\?": use previous search pattern.
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -3600,6 +3600,15 @@ find_ex_command(
 	    }
 	}
 
+	// "g:", "s:" and "l:" are always assumed to be a variable, thus start
+	// an expression.  A global/substitute/list command needs to use a
+	// longer name.
+	if (vim_strchr((char_u *)"gsl", *p) != NULL && p[1] == ':')
+	{
+	    eap->cmdidx = CMD_eval;
+	    return eap->cmd;
+	}
+
 	// If it is an ID it might be a variable with an operator on the next
 	// line, if the variable exists it can't be an Ex command.
 	if (p > eap->cmd && ends_excmd(*skipwhite(p))
--- a/src/proto/vim9compile.pro
+++ b/src/proto/vim9compile.pro
@@ -17,6 +17,7 @@ void fill_exarg_from_cctx(exarg_T *eap, 
 int assignment_len(char_u *p, int *heredoc);
 void vim9_declare_error(char_u *name);
 int check_vim9_unlet(char_u *name);
+int check_global_and_subst(char_u *cmd, char_u *arg);
 int compile_def_function(ufunc_T *ufunc, int check_return_type, compiletype_T compile_type, cctx_T *outer_cctx);
 void set_function_type(ufunc_T *ufunc);
 void delete_instr(isn_T *isn);
--- a/src/testdir/test_vim9_cmd.vim
+++ b/src/testdir/test_vim9_cmd.vim
@@ -1489,5 +1489,54 @@ def Test_cmdwin_block()
   au! justTesting
 enddef
 
+def Test_var_not_cmd()
+  var lines =<< trim END
+      g:notexist:cmd
+  END
+  CheckDefAndScriptFailure2(lines, 'E488: Trailing characters: :cmd', 'E121: Undefined variable: g:notexist', 1)
+
+  lines =<< trim END
+      g-pat-cmd
+  END
+  CheckDefAndScriptFailure(lines, 'E1241:', 1)
+
+  lines =<< trim END
+      s:notexist:repl
+  END
+  CheckDefAndScriptFailure2(lines, 'E488: Trailing characters: :repl', 'E121: Undefined variable: s:notexist', 1)
+
+  lines =<< trim END
+      s-pat-repl
+  END
+  CheckDefAndScriptFailure(lines, 'E1241:', 1)
+
+  lines =<< trim END
+      w:notexist->len()
+  END
+  CheckDefExecAndScriptFailure(lines, 'E121: Undefined variable: w:notexist', 1)
+
+  lines =<< trim END
+      b:notexist->len()
+  END
+  CheckDefExecAndScriptFailure(lines, 'E121: Undefined variable: b:notexist', 1)
+
+  lines =<< trim END
+      t:notexist->len()
+  END
+  CheckDefExecAndScriptFailure(lines, 'E121: Undefined variable: t:notexist', 1)
+enddef
+
+def Test_no_space_after_command()
+  var lines =<< trim END
+      g /pat/cmd
+  END
+  CheckDefAndScriptFailure(lines, 'E1242:', 1)
+
+  lines =<< trim END
+      s /pat/repl
+  END
+  CheckDefAndScriptFailure(lines, 'E1242:', 1)
+enddef
+
 
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3503,
+/**/
     3502,
 /**/
     3501,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -9473,6 +9473,26 @@ compile_cexpr(char_u *line, exarg_T *eap
 #endif
 
 /*
+ * Check if the separator for a :global or :substitute command is OK.
+ */
+    int
+check_global_and_subst(char_u *cmd, char_u *arg)
+{
+    if (arg == cmd + 1 && vim_strchr(":-.", *arg) != NULL)
+    {
+	semsg(_(e_separator_not_supported_str), arg);
+	return FAIL;
+    }
+    if (VIM_ISWHITE(cmd[1]))
+    {
+	semsg(_(e_no_white_space_allowed_before_separator_str), cmd);
+	return FAIL;
+    }
+    return OK;
+}
+
+
+/*
  * Add a function to the list of :def functions.
  * This sets "ufunc->uf_dfunc_idx" but the function isn't compiled yet.
  */
@@ -10066,6 +10086,8 @@ compile_def_function(
 		    break;
 
 	    case CMD_substitute:
+		    if (check_global_and_subst(ea.cmd, p) == FAIL)
+			goto erret;
 		    if (cctx.ctx_skip == SKIP_YES)
 			line = (char_u *)"";
 		    else
@@ -10132,6 +10154,10 @@ compile_def_function(
 			line = compile_script(line, &cctx);
 		    break;
 
+	    case CMD_global:
+		    if (check_global_and_subst(ea.cmd, p) == FAIL)
+			goto erret;
+		    // FALLTHROUGH
 	    default:
 		    // Not recognized, execute with do_cmdline_cmd().
 		    ea.arg = p;