changeset 21937:b931df03adcc v8.2.1518

patch 8.2.1518: Vim9: cannot assign to local option Commit: https://github.com/vim/vim/commit/2e80095501238e0c6b702ac7cdfa2e2b763dba28 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Aug 23 19:34:48 2020 +0200 patch 8.2.1518: Vim9: cannot assign to local option Problem: Vim9: cannot assign to local option. Solution: Skip over "&l:" and "&g:". (closes https://github.com/vim/vim/issues/6749)
author Bram Moolenaar <Bram@vim.org>
date Sun, 23 Aug 2020 19:45:03 +0200
parents 8303e21ae158
children 9131ebc53d38
files src/ex_docmd.c src/proto/ex_docmd.pro src/testdir/test_vim9_script.vim src/testdir/vim9.vim src/version.c src/vim9compile.c
diffstat 6 files changed, 51 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -3243,6 +3243,27 @@ append_command(char_u *cmd)
 }
 
 /*
+ * If "start" points "&opt", "&l:opt", "&g:opt" or "$ENV" return a pointer to
+ * the name.  Otherwise just return "start".
+ */
+    char_u *
+skip_option_env_lead(char_u *start)
+{
+    char_u *name = start;
+
+    if (*start == '&')
+    {
+	if ((start[1] == 'l' || start[1] == 'g') && start[2] == ':')
+	    name += 3;
+	else
+	    name += 1;
+    }
+    else if (*start == '$')
+	name += 1;
+    return name;
+}
+
+/*
  * Find an Ex command by its name, either built-in or user.
  * Start of the name can be found at eap->cmd.
  * Sets eap->cmdidx and returns a pointer to char after the command name.
@@ -3273,9 +3294,7 @@ find_ex_command(
     p = eap->cmd;
     if (lookup != NULL)
     {
-	// Skip over first char for "&opt = val", "$ENV = val" and "@r = val".
-	char_u *pskip = (*eap->cmd == '&' || *eap->cmd == '$')
-						     ? eap->cmd + 1 : eap->cmd;
+	char_u *pskip = skip_option_env_lead(eap->cmd);
 
 	if (vim_strchr((char_u *)"{('[\"@", *p) != NULL
 	       || ((p = to_name_const_end(pskip)) > eap->cmd && *p != NUL))
--- a/src/proto/ex_docmd.pro
+++ b/src/proto/ex_docmd.pro
@@ -10,6 +10,7 @@ int parse_command_modifiers(exarg_T *eap
 void undo_cmdmod(exarg_T *eap, 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);
 char_u *find_ex_command(exarg_T *eap, int *full, void *(*lookup)(char_u *, size_t, cctx_T *), cctx_T *cctx);
 int modifier_len(char_u *cmd);
 int cmd_exists(char_u *name);
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -110,12 +110,21 @@ def Test_assignment()
   endif
 
   lines =<< trim END
-    vim9script
     &ts = 6
     &ts += 3
     assert_equal(9, &ts)
+
+    &l:ts = 6
+    assert_equal(6, &ts)
+    &l:ts += 2
+    assert_equal(8, &ts)
+
+    &g:ts = 6
+    assert_equal(6, &g:ts)
+    &g:ts += 2
+    assert_equal(8, &g:ts)
   END
-  CheckScriptSuccess(lines)
+  CheckDefAndScriptSuccess(lines)
 
   CheckDefFailure(['&notex += 3'], 'E113:')
   CheckDefFailure(['&ts ..= "xxx"'], 'E1019:')
@@ -163,19 +172,15 @@ def Test_assignment()
   call CheckDefFailure(['$SOME_ENV_VAR += "more"'], 'E1051:')
   call CheckDefFailure(['$SOME_ENV_VAR += 123'], 'E1012:')
 
-  @a = 'areg'
-  @a ..= 'add'
-  assert_equal('aregadd', @a)
-  call CheckDefFailure(['@a += "more"'], 'E1051:')
-  call CheckDefFailure(['@a += 123'], 'E1012:')
-
   lines =<< trim END
-    vim9script
     @c = 'areg'
     @c ..= 'add'
     assert_equal('aregadd', @c)
   END
-  call CheckScriptSuccess(lines)
+  CheckDefAndScriptSuccess(lines)
+
+  call CheckDefFailure(['@a += "more"'], 'E1051:')
+  call CheckDefFailure(['@a += 123'], 'E1012:')
 
   v:errmsg = 'none'
   v:errmsg ..= 'again'
--- a/src/testdir/vim9.vim
+++ b/src/testdir/vim9.vim
@@ -41,6 +41,11 @@ def CheckScriptSuccess(lines: list<strin
   delete('Xdef')
 enddef
 
+def CheckDefAndScriptSuccess(lines: list<string>)
+  CheckDefSuccess(lines)
+  CheckScriptSuccess(['vim9script'] + lines)
+enddef
+
 " Check that a command fails both when used in a :def function and when used
 " in Vim9 script.
 def CheckScriptAndDefFailure(lines: list<string>, error: string, lnum = -3)
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1518,
+/**/
     1517,
 /**/
     1516,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -4550,8 +4550,8 @@ compile_assignment(char_u *arg, exarg_T 
 	    p = var_start + 2;
 	else
 	{
-	    p = (*var_start == '&' || *var_start == '$')
-						   ? var_start + 1 : var_start;
+	    // skip over the leading "&", "&l:", "&g:" and "$"
+	    p = skip_option_env_lead(var_start);
 	    p = to_name_end(p, TRUE);
 	}
 
@@ -4595,8 +4595,8 @@ compile_assignment(char_u *arg, exarg_T 
 		}
 		cc = *p;
 		*p = NUL;
-		opt_type = get_option_value(var_start + 1, &numval,
-							      NULL, opt_flags);
+		opt_type = get_option_value(skip_option_env_lead(var_start),
+						     &numval, NULL, opt_flags);
 		*p = cc;
 		if (opt_type == -3)
 		{
@@ -5131,7 +5131,8 @@ compile_assignment(char_u *arg, exarg_T 
 	    switch (dest)
 	    {
 		case dest_option:
-		    generate_STOREOPT(cctx, name + 1, opt_flags);
+		    generate_STOREOPT(cctx, skip_option_env_lead(name),
+								    opt_flags);
 		    break;
 		case dest_global:
 		    // include g: with the name, easier to execute that way