forked from aniani/vim
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>
This commit is contained in:
committed by
Christian Brabandt
parent
7398f367d5
commit
edcba96c00
@@ -2520,8 +2520,8 @@ EXTERN char e_no_line_number_to_use_for_sflnum[]
|
|||||||
INIT(= N_("E961: No line number to use for \"<sflnum>\""));
|
INIT(= N_("E961: No line number to use for \"<sflnum>\""));
|
||||||
EXTERN char e_invalid_action_str_2[]
|
EXTERN char e_invalid_action_str_2[]
|
||||||
INIT(= N_("E962: Invalid action: '%s'"));
|
INIT(= N_("E962: Invalid action: '%s'"));
|
||||||
EXTERN char e_setting_str_to_value_with_wrong_type[]
|
EXTERN char e_setting_v_str_to_value_with_wrong_type[]
|
||||||
INIT(= N_("E963: Setting %s to value with wrong type"));
|
INIT(= N_("E963: Setting v:%s to value with wrong type"));
|
||||||
#endif
|
#endif
|
||||||
#ifdef FEAT_PROP_POPUP
|
#ifdef FEAT_PROP_POPUP
|
||||||
EXTERN char_u e_invalid_column_number_nr[]
|
EXTERN char_u e_invalid_column_number_nr[]
|
||||||
|
106
src/evalvars.c
106
src/evalvars.c
@@ -3672,6 +3672,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".
|
* Set variable "name" to value in "tv".
|
||||||
* If the variable already exists, the value is updated.
|
* If the variable already exists, the value is updated.
|
||||||
@@ -3877,51 +3933,15 @@ set_var_const(
|
|||||||
|
|
||||||
// existing variable, need to clear the value
|
// 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.
|
// 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)
|
if (type_error)
|
||||||
{
|
semsg(_(e_setting_v_str_to_value_with_wrong_type), varname);
|
||||||
VIM_CLEAR(di->di_tv.vval.v_string);
|
goto failed;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_tv(&di->di_tv);
|
clear_tv(&di->di_tv);
|
||||||
|
10
src/if_lua.c
10
src/if_lua.c
@@ -1900,6 +1900,16 @@ luaV_setvar(lua_State *L)
|
|||||||
}
|
}
|
||||||
else
|
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 the old value
|
||||||
clear_tv(&di->di_tv);
|
clear_tv(&di->di_tv);
|
||||||
// Update the value
|
// Update the value
|
||||||
|
@@ -75,6 +75,7 @@ void unref_var_dict(dict_T *dict);
|
|||||||
void vars_clear(hashtab_T *ht);
|
void vars_clear(hashtab_T *ht);
|
||||||
void vars_clear_ext(hashtab_T *ht, int free_val);
|
void vars_clear_ext(hashtab_T *ht, int free_val);
|
||||||
void delete_var(hashtab_T *ht, hashitem_T *hi);
|
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(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);
|
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);
|
int var_check_permission(dictitem_T *di, char_u *name);
|
||||||
|
@@ -970,9 +970,9 @@ func Test_lua_global_var_table()
|
|||||||
let g:Var1 = [10, 20]
|
let g:Var1 = [10, 20]
|
||||||
let g:Var2 = #{one: 'mercury', two: 'mars'}
|
let g:Var2 = #{one: 'mercury', two: 'mars'}
|
||||||
lua << trim END
|
lua << trim END
|
||||||
vim.g.Var1[2] = Nil
|
vim.g.Var1[2] = nil
|
||||||
vim.g.Var1[3] = 15
|
vim.g.Var1[3] = 15
|
||||||
vim.g.Var2['two'] = Nil
|
vim.g.Var2['two'] = nil
|
||||||
vim.g.Var2['three'] = 'earth'
|
vim.g.Var2['three'] = 'earth'
|
||||||
END
|
END
|
||||||
call assert_equal([10, 15], g:Var1)
|
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:Var4 = #{x: 'edit', y: 'run'}
|
||||||
let g:Var5 = function('min')
|
let g:Var5 = function('min')
|
||||||
lua << trim END
|
lua << trim END
|
||||||
vim.g.Var1 = Nil
|
vim.g.Var1 = nil
|
||||||
vim.g.Var2 = Nil
|
vim.g.Var2 = nil
|
||||||
vim.g.Var3 = Nil
|
vim.g.Var3 = nil
|
||||||
vim.g.Var4 = Nil
|
vim.g.Var4 = nil
|
||||||
vim.g.Var5 = Nil
|
vim.g.Var5 = nil
|
||||||
END
|
END
|
||||||
call assert_false(exists('g:Var1'))
|
call assert_false(exists('g:Var1'))
|
||||||
call assert_false(exists('g:Var2'))
|
call assert_false(exists('g:Var2'))
|
||||||
@@ -1001,16 +1001,16 @@ func Test_lua_global_var_table()
|
|||||||
let g:Var1 = 10
|
let g:Var1 = 10
|
||||||
lockvar g:Var1
|
lockvar g:Var1
|
||||||
call assert_fails('lua vim.g.Var1 = 20', 'variable is locked')
|
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
|
unlockvar g:Var1
|
||||||
let g:Var2 = [7, 14]
|
let g:Var2 = [7, 14]
|
||||||
lockvar 0 g:Var2
|
lockvar 0 g:Var2
|
||||||
lua vim.g.Var2[2] = Nil
|
lua vim.g.Var2[2] = nil
|
||||||
lua vim.g.Var2[3] = 21
|
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)
|
call assert_equal([7, 21], g:Var2)
|
||||||
lockvar 1 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')
|
call assert_fails('lua vim.g.Var2[3] = 21', 'list is locked')
|
||||||
unlockvar g:Var2
|
unlockvar g:Var2
|
||||||
|
|
||||||
@@ -1020,7 +1020,7 @@ func Test_lua_global_var_table()
|
|||||||
|
|
||||||
" Attempt to access a non-existing global variable
|
" Attempt to access a non-existing global variable
|
||||||
call assert_equal(v:null, luaeval('vim.g.NonExistingVar'))
|
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
|
unlet! g:Var1 g:Var2 g:Var3 g:Var4 g:Var5
|
||||||
endfunc
|
endfunc
|
||||||
@@ -1033,14 +1033,36 @@ func Test_lua_predefined_var_table()
|
|||||||
call assert_equal('SomeError', luaeval('vim.v.errmsg'))
|
call assert_equal('SomeError', luaeval('vim.v.errmsg'))
|
||||||
lua vim.v.errmsg = 'OtherError'
|
lua vim.v.errmsg = 'OtherError'
|
||||||
call assert_equal('OtherError', v:errmsg)
|
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']
|
let v:oldfiles = ['one', 'two']
|
||||||
call assert_equal(['one', 'two'], luaeval('vim.v.oldfiles'))
|
call assert_equal(['one', 'two'], luaeval('vim.v.oldfiles'))
|
||||||
lua vim.v.oldfiles = vim.list({})
|
lua vim.v.oldfiles = vim.list({})
|
||||||
call assert_equal([], v:oldfiles)
|
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_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')
|
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
|
endfunc
|
||||||
|
|
||||||
" Test for adding, accessing and modifying window-local variables using the
|
" 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)
|
call assert_equal(#{a: [1, 2], b: 20}, w:wvar7)
|
||||||
|
|
||||||
" delete a window variable
|
" delete a window variable
|
||||||
lua vim.w.wvar2 = Nil
|
lua vim.w.wvar2 = nil
|
||||||
call assert_false(exists('w:wvar2'))
|
call assert_false(exists('w:wvar2'))
|
||||||
|
|
||||||
new
|
new
|
||||||
@@ -1117,7 +1139,7 @@ func Test_lua_buffer_var_table()
|
|||||||
call assert_equal(#{a: [1, 2], b: 20}, b:bvar7)
|
call assert_equal(#{a: [1, 2], b: 20}, b:bvar7)
|
||||||
|
|
||||||
" delete a buffer variable
|
" delete a buffer variable
|
||||||
lua vim.b.bvar2 = Nil
|
lua vim.b.bvar2 = nil
|
||||||
call assert_false(exists('b:bvar2'))
|
call assert_false(exists('b:bvar2'))
|
||||||
|
|
||||||
new
|
new
|
||||||
@@ -1158,7 +1180,7 @@ func Test_lua_tabpage_var_table()
|
|||||||
call assert_equal(#{a: [1, 2], b: 20}, t:tvar7)
|
call assert_equal(#{a: [1, 2], b: 20}, t:tvar7)
|
||||||
|
|
||||||
" delete a tabpage variable
|
" delete a tabpage variable
|
||||||
lua vim.t.tvar2 = Nil
|
lua vim.t.tvar2 = nil
|
||||||
call assert_false(exists('t:tvar2'))
|
call assert_false(exists('t:tvar2'))
|
||||||
|
|
||||||
tabnew
|
tabnew
|
||||||
|
@@ -699,6 +699,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1933,
|
||||||
/**/
|
/**/
|
||||||
1932,
|
1932,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user