changeset 33339:441ceb1c45c1 v9.0.1933

patch 9.0.1933: Can change the type of a v: variable using if_lua Commit: https://github.com/vim/vim/commit/edcba96c0088210927558b0e2583f3b689f457c4 Author: zeertzjq <zeertzjq@outlook.com> Date: Sun Sep 24 23:13:51 2023 +0200 patch 9.0.1933: Can change the type of a v: variable using if_lua Problem: Can change the type of a v: variable using if_lua. Solution: Add additional handling of v: variables like :let. closes: #13161 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: zeertzjq <zeertzjq@outlook.com>
author Christian Brabandt <cb@256bit.org>
date Sun, 24 Sep 2023 23:30:03 +0200
parents 9e4fde36162a
children 6ea17e0f7de4
files src/errors.h src/evalvars.c src/if_lua.c src/proto/evalvars.pro src/testdir/test_lua.vim src/version.c
diffstat 6 files changed, 117 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- a/src/errors.h
+++ b/src/errors.h
@@ -2520,8 +2520,8 @@ EXTERN char e_no_line_number_to_use_for_
 	INIT(= N_("E961: No line number to use for \"<sflnum>\""));
 EXTERN char e_invalid_action_str_2[]
 	INIT(= N_("E962: Invalid action: '%s'"));
-EXTERN char e_setting_str_to_value_with_wrong_type[]
-	INIT(= N_("E963: Setting %s to value with wrong type"));
+EXTERN char e_setting_v_str_to_value_with_wrong_type[]
+	INIT(= N_("E963: Setting v:%s to value with wrong type"));
 #endif
 #ifdef FEAT_PROP_POPUP
 EXTERN char_u e_invalid_column_number_nr[]
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -3673,6 +3673,62 @@ list_one_var_a(
 }
 
 /*
+ * Addition handling for setting a v: variable.
+ * Return TRUE if the variable should be set normally,
+ *        FALSE if nothing else needs to be done.
+ */
+    int
+before_set_vvar(
+    char_u	*varname,
+    dictitem_T	*di,
+    typval_T	*tv,
+    int		copy,
+    int		*type_error)
+{
+    if (di->di_tv.v_type == VAR_STRING)
+    {
+	VIM_CLEAR(di->di_tv.vval.v_string);
+	if (copy || tv->v_type != VAR_STRING)
+	{
+	    char_u *val = tv_get_string(tv);
+
+	    // Careful: when assigning to v:errmsg and
+	    // tv_get_string() causes an error message the variable
+	    // will already be set.
+	    if (di->di_tv.vval.v_string == NULL)
+		di->di_tv.vval.v_string = vim_strsave(val);
+	}
+	else
+	{
+	    // Take over the string to avoid an extra alloc/free.
+	    di->di_tv.vval.v_string = tv->vval.v_string;
+	    tv->vval.v_string = NULL;
+	}
+	return FALSE;
+    }
+    else if (di->di_tv.v_type == VAR_NUMBER)
+    {
+	di->di_tv.vval.v_number = tv_get_number(tv);
+	if (STRCMP(varname, "searchforward") == 0)
+	    set_search_direction(di->di_tv.vval.v_number ? '/' : '?');
+#ifdef FEAT_SEARCH_EXTRA
+	else if (STRCMP(varname, "hlsearch") == 0)
+	{
+	    no_hlsearch = !di->di_tv.vval.v_number;
+	    redraw_all_later(UPD_SOME_VALID);
+	}
+#endif
+	return FALSE;
+    }
+    else if (di->di_tv.v_type != tv->v_type)
+    {
+	*type_error = TRUE;
+	return FALSE;
+    }
+    return TRUE;
+}
+
+/*
  * Set variable "name" to value in "tv".
  * If the variable already exists, the value is updated.
  * Otherwise the variable is created.
@@ -3877,51 +3933,15 @@ set_var_const(
 
 	// existing variable, need to clear the value
 
-	// Handle setting internal di: variables separately where needed to
+	// Handle setting internal v: variables separately where needed to
 	// prevent changing the type.
-	if (ht == &vimvarht)
+	int type_error = FALSE;
+	if (ht == &vimvarht
+		&& !before_set_vvar(varname, di, tv, copy, &type_error))
 	{
-	    if (di->di_tv.v_type == VAR_STRING)
-	    {
-		VIM_CLEAR(di->di_tv.vval.v_string);
-		if (copy || tv->v_type != VAR_STRING)
-		{
-		    char_u *val = tv_get_string(tv);
-
-		    // Careful: when assigning to v:errmsg and
-		    // tv_get_string() causes an error message the variable
-		    // will already be set.
-		    if (di->di_tv.vval.v_string == NULL)
-			di->di_tv.vval.v_string = vim_strsave(val);
-		}
-		else
-		{
-		    // Take over the string to avoid an extra alloc/free.
-		    di->di_tv.vval.v_string = tv->vval.v_string;
-		    tv->vval.v_string = NULL;
-		}
-		goto failed;
-	    }
-	    else if (di->di_tv.v_type == VAR_NUMBER)
-	    {
-		di->di_tv.vval.v_number = tv_get_number(tv);
-		if (STRCMP(varname, "searchforward") == 0)
-		    set_search_direction(di->di_tv.vval.v_number
-							      ? '/' : '?');
-#ifdef FEAT_SEARCH_EXTRA
-		else if (STRCMP(varname, "hlsearch") == 0)
-		{
-		    no_hlsearch = !di->di_tv.vval.v_number;
-		    redraw_all_later(UPD_SOME_VALID);
-		}
-#endif
-		goto failed;
-	    }
-	    else if (di->di_tv.v_type != tv->v_type)
-	    {
-		semsg(_(e_setting_str_to_value_with_wrong_type), name);
-		goto failed;
-	    }
+	    if (type_error)
+		semsg(_(e_setting_v_str_to_value_with_wrong_type), varname);
+	    goto failed;
 	}
 
 	clear_tv(&di->di_tv);
--- a/src/if_lua.c
+++ b/src/if_lua.c
@@ -1900,6 +1900,16 @@ luaV_setvar(lua_State *L)
 	}
 	else
 	{
+	    int type_error = FALSE;
+	    if (dict == get_vimvar_dict()
+	       && !before_set_vvar((char_u *)name, di, &tv, TRUE, &type_error))
+	    {
+		clear_tv(&tv);
+		if (type_error)
+		    return luaL_error(L,
+				"Setting v:%s to value with wrong type", name);
+		return 0;
+	    }
 	    // Clear the old value
 	    clear_tv(&di->di_tv);
 	    // Update the value
--- a/src/proto/evalvars.pro
+++ b/src/proto/evalvars.pro
@@ -75,6 +75,7 @@ void unref_var_dict(dict_T *dict);
 void vars_clear(hashtab_T *ht);
 void vars_clear_ext(hashtab_T *ht, int free_val);
 void delete_var(hashtab_T *ht, hashitem_T *hi);
+int before_set_vvar(char_u *varname, dictitem_T *di, typval_T *tv, int copy, int *type_error);
 void set_var(char_u *name, typval_T *tv, int copy);
 void set_var_const(char_u *name, scid_T sid, type_T *type_arg, typval_T *tv_arg, int copy, int flags_arg, int var_idx);
 int var_check_permission(dictitem_T *di, char_u *name);
--- a/src/testdir/test_lua.vim
+++ b/src/testdir/test_lua.vim
@@ -970,9 +970,9 @@ func Test_lua_global_var_table()
   let g:Var1 = [10, 20]
   let g:Var2 = #{one: 'mercury', two: 'mars'}
   lua << trim END
-    vim.g.Var1[2] = Nil
+    vim.g.Var1[2] = nil
     vim.g.Var1[3] = 15
-    vim.g.Var2['two'] = Nil
+    vim.g.Var2['two'] = nil
     vim.g.Var2['three'] = 'earth'
   END
   call assert_equal([10, 15], g:Var1)
@@ -985,11 +985,11 @@ func Test_lua_global_var_table()
   let g:Var4 = #{x: 'edit', y: 'run'}
   let g:Var5 = function('min')
   lua << trim END
-    vim.g.Var1 = Nil
-    vim.g.Var2 = Nil
-    vim.g.Var3 = Nil
-    vim.g.Var4 = Nil
-    vim.g.Var5 = Nil
+    vim.g.Var1 = nil
+    vim.g.Var2 = nil
+    vim.g.Var3 = nil
+    vim.g.Var4 = nil
+    vim.g.Var5 = nil
   END
   call assert_false(exists('g:Var1'))
   call assert_false(exists('g:Var2'))
@@ -1001,16 +1001,16 @@ func Test_lua_global_var_table()
   let g:Var1 = 10
   lockvar g:Var1
   call assert_fails('lua vim.g.Var1 = 20', 'variable is locked')
-  call assert_fails('lua vim.g.Var1 = Nil', 'variable is locked')
+  call assert_fails('lua vim.g.Var1 = nil', 'variable is locked')
   unlockvar g:Var1
   let g:Var2 = [7, 14]
   lockvar 0 g:Var2
-  lua vim.g.Var2[2] = Nil
+  lua vim.g.Var2[2] = nil
   lua vim.g.Var2[3] = 21
-  call assert_fails('lua vim.g.Var2 = Nil', 'variable is locked')
+  call assert_fails('lua vim.g.Var2 = nil', 'variable is locked')
   call assert_equal([7, 21], g:Var2)
   lockvar 1 g:Var2
-  call assert_fails('lua vim.g.Var2[2] = Nil', 'list is locked')
+  call assert_fails('lua vim.g.Var2[2] = nil', 'list is locked')
   call assert_fails('lua vim.g.Var2[3] = 21', 'list is locked')
   unlockvar g:Var2
 
@@ -1020,7 +1020,7 @@ func Test_lua_global_var_table()
 
   " Attempt to access a non-existing global variable
   call assert_equal(v:null, luaeval('vim.g.NonExistingVar'))
-  lua vim.g.NonExisting = Nil
+  lua vim.g.NonExisting = nil
 
   unlet! g:Var1 g:Var2 g:Var3 g:Var4 g:Var5
 endfunc
@@ -1033,14 +1033,36 @@ func Test_lua_predefined_var_table()
   call assert_equal('SomeError', luaeval('vim.v.errmsg'))
   lua vim.v.errmsg = 'OtherError'
   call assert_equal('OtherError', v:errmsg)
-  call assert_fails('lua vim.v.errmsg = Nil', 'variable is fixed')
+  lua vim.v.errmsg = 42
+  call assert_equal('42', v:errmsg)
+  call assert_fails('lua vim.v.errmsg = nil', 'variable is fixed')
   let v:oldfiles = ['one', 'two']
   call assert_equal(['one', 'two'], luaeval('vim.v.oldfiles'))
   lua vim.v.oldfiles = vim.list({})
   call assert_equal([], v:oldfiles)
+  call assert_fails('lua vim.v.oldfiles = "a"',
+        \ 'Setting v:oldfiles to value with wrong type')
+  call assert_equal([], v:oldfiles)
   call assert_equal(v:null, luaeval('vim.v.null'))
-  call assert_fails('lua vim.v.argv[1] = Nil', 'list is locked')
+  call assert_fails('lua vim.v.argv[1] = nil', 'list is locked')
   call assert_fails('lua vim.v.newvar = 1', 'Dictionary is locked')
+
+  new
+  call setline(1, ' foo foo foo')
+  /foo
+  call assert_equal([0, 1, 2, 0, 2], getcurpos())
+  call assert_equal(1, v:searchforward)
+  normal! n
+  call assert_equal([0, 1, 6, 0, 6], getcurpos())
+  lua vim.v.searchforward = 0
+  call assert_equal(0, v:searchforward)
+  normal! n
+  call assert_equal([0, 1, 2, 0, 2], getcurpos())
+  lua vim.v.searchforward = 1
+  call assert_equal(1, v:searchforward)
+  normal! n
+  call assert_equal([0, 1, 6, 0, 6], getcurpos())
+  bwipe!
 endfunc
 
 " Test for adding, accessing and modifying window-local variables using the
@@ -1076,7 +1098,7 @@ func Test_lua_window_var_table()
   call assert_equal(#{a: [1, 2], b: 20}, w:wvar7)
 
   " delete a window variable
-  lua vim.w.wvar2 = Nil
+  lua vim.w.wvar2 = nil
   call assert_false(exists('w:wvar2'))
 
   new
@@ -1117,7 +1139,7 @@ func Test_lua_buffer_var_table()
   call assert_equal(#{a: [1, 2], b: 20}, b:bvar7)
 
   " delete a buffer variable
-  lua vim.b.bvar2 = Nil
+  lua vim.b.bvar2 = nil
   call assert_false(exists('b:bvar2'))
 
   new
@@ -1158,7 +1180,7 @@ func Test_lua_tabpage_var_table()
   call assert_equal(#{a: [1, 2], b: 20}, t:tvar7)
 
   " delete a tabpage variable
-  lua vim.t.tvar2 = Nil
+  lua vim.t.tvar2 = nil
   call assert_false(exists('t:tvar2'))
 
   tabnew
--- a/src/version.c
+++ b/src/version.c
@@ -700,6 +700,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1933,
+/**/
     1932,
 /**/
     1931,