0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

patch 8.2.4861: it is not easy to restore saved mappings

Problem:    It is not easy to restore saved mappings.
Solution:   Make mapset() accept a dict argument. (Ernie Rael, closes #10295)
This commit is contained in:
Ernie Rael 2022-05-04 15:40:22 +01:00 committed by Bram Moolenaar
parent 05cf63e9bd
commit 51d04d16f2
9 changed files with 323 additions and 29 deletions

View File

@ -5310,6 +5310,7 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
"lnum" The line number in "sid", zero if unknown. "lnum" The line number in "sid", zero if unknown.
"nowait" Do not wait for other, longer mappings. "nowait" Do not wait for other, longer mappings.
(|:map-<nowait>|). (|:map-<nowait>|).
"abbr" True if this is an |abbreviation|.
The dictionary can be used to restore a mapping with The dictionary can be used to restore a mapping with
|mapset()|. |mapset()|.
@ -5380,9 +5381,18 @@ mapnew({expr1}, {expr2}) *mapnew()*
mapset({mode}, {abbr}, {dict}) *mapset()* mapset({mode}, {abbr}, {dict}) *mapset()*
Restore a mapping from a dictionary returned by |maparg()|. mapset({dict})
{mode} and {abbr} should be the same as for the call to Restore a mapping from a dictionary, possibly returned by
|maparg()|. *E460* |maparg()| or |maplist()|. A buffer mapping, when dict.buffer
is true, is set on the current buffer; it is up to the caller
to insure that the intended buffer is the current buffer. This
feature allows copying mappings from one buffer to another.
The dict.mode value may restore a single mapping that covers
more than one mode, like with mode values of '!', ' ', 'nox',
or 'v'. *E1276*
In the first form, {mode} and {abbr} should be the same as
for the call to |maparg()|. *E460*
{mode} is used to define the mode in which the mapping is set, {mode} is used to define the mode in which the mapping is set,
not the "mode" entry in {dict}. not the "mode" entry in {dict}.
Example for saving and restoring a mapping: > Example for saving and restoring a mapping: >
@ -5391,8 +5401,22 @@ mapset({mode}, {abbr}, {dict}) *mapset()*
... ...
call mapset('n', 0, save_map) call mapset('n', 0, save_map)
< Note that if you are going to replace a map in several modes, < Note that if you are going to replace a map in several modes,
e.g. with `:map!`, you need to save the mapping for all of e.g. with `:map!`, you need to save/restore the mapping for
them, since they can differ. all of them, when they might differ.
In the second form, with {dict} as the only argument, mode
and abbr are taken from the dict.
Example: >
vim9script
var save_maps = maplist()->filter(
(_, m) => m.lhs == 'K')
nnoremap K somethingelse
cnoremap K somethingelse2
# ...
unmap K
for d in save_maps
mapset(d)
endfor
match({expr}, {pat} [, {start} [, {count}]]) *match()* match({expr}, {pat} [, {start} [, {count}]]) *match()*

View File

@ -3262,4 +3262,6 @@ EXTERN char e_no_script_file_name_to_substitute_for_script[]
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
EXTERN char e_string_or_function_required_for_arrow_parens_expr[] EXTERN char e_string_or_function_required_for_arrow_parens_expr[]
INIT(= N_("E1275: String or function required for ->(expr)")); INIT(= N_("E1275: String or function required for ->(expr)"));
EXTERN char e_illegal_map_mode_string_str[]
INIT(= N_("E1276: Illegal map mode string: '%s'"));
#endif #endif

View File

@ -421,6 +421,21 @@ arg_string_or_list_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *con
return FAIL; return FAIL;
} }
/*
* Check "type" is a string or a dict of 'any'
*/
static int
arg_string_or_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
{
if (type->tt_type == VAR_ANY
|| type->tt_type == VAR_UNKNOWN
|| type->tt_type == VAR_STRING
|| type->tt_type == VAR_DICT)
return OK;
arg_type_mismatch(&t_string, type, context->arg_idx + 1);
return FAIL;
}
/* /*
* Check "type" is a string or a blob * Check "type" is a string or a blob
*/ */
@ -998,8 +1013,8 @@ static argcheck_T arg3_string[] = {arg_string, arg_string, arg_string};
static argcheck_T arg3_string_any_dict[] = {arg_string, NULL, arg_dict_any}; static argcheck_T arg3_string_any_dict[] = {arg_string, NULL, arg_dict_any};
static argcheck_T arg3_string_any_string[] = {arg_string, NULL, arg_string}; static argcheck_T arg3_string_any_string[] = {arg_string, NULL, arg_string};
static argcheck_T arg3_string_bool_bool[] = {arg_string, arg_bool, arg_bool}; static argcheck_T arg3_string_bool_bool[] = {arg_string, arg_bool, arg_bool};
static argcheck_T arg3_string_bool_dict[] = {arg_string, arg_bool, arg_dict_any};
static argcheck_T arg3_string_number_bool[] = {arg_string, arg_number, arg_bool}; static argcheck_T arg3_string_number_bool[] = {arg_string, arg_number, arg_bool};
static argcheck_T arg3_string_or_dict_bool_dict[] = {arg_string_or_dict_any, arg_bool, arg_dict_any};
static argcheck_T arg3_string_string_bool[] = {arg_string, arg_string, arg_bool}; static argcheck_T arg3_string_string_bool[] = {arg_string, arg_string, arg_bool};
static argcheck_T arg3_string_string_dict[] = {arg_string, arg_string, arg_dict_any}; static argcheck_T arg3_string_string_dict[] = {arg_string, arg_string, arg_dict_any};
static argcheck_T arg3_string_string_number[] = {arg_string, arg_string, arg_number}; static argcheck_T arg3_string_string_number[] = {arg_string, arg_string, arg_number};
@ -2053,7 +2068,7 @@ static funcentry_T global_functions[] =
ret_list_dict_any, f_maplist}, ret_list_dict_any, f_maplist},
{"mapnew", 2, 2, FEARG_1, arg2_mapnew, {"mapnew", 2, 2, FEARG_1, arg2_mapnew,
ret_first_cont, f_mapnew}, ret_first_cont, f_mapnew},
{"mapset", 3, 3, FEARG_1, arg3_string_bool_dict, {"mapset", 1, 3, FEARG_1, arg3_string_or_dict_bool_dict,
ret_void, f_mapset}, ret_void, f_mapset},
{"match", 2, 4, FEARG_1, arg24_match_func, {"match", 2, 4, FEARG_1, arg24_match_func,
ret_any, f_match}, ret_any, f_match},

View File

@ -2283,7 +2283,8 @@ mapblock2dict(
mapblock_T *mp, mapblock_T *mp,
dict_T *dict, dict_T *dict,
char_u *lhsrawalt, // may be NULL char_u *lhsrawalt, // may be NULL
int buffer_local) // false if not buffer local mapping int buffer_local, // false if not buffer local mapping
int abbr) // true if abbreviation
{ {
char_u *lhs = str2special_save(mp->m_keys, TRUE); char_u *lhs = str2special_save(mp->m_keys, TRUE);
char_u *mapmode = map_mode_to_chars(mp->m_mode); char_u *mapmode = map_mode_to_chars(mp->m_mode);
@ -2307,6 +2308,7 @@ mapblock2dict(
dict_add_number(dict, "buffer", (long)buffer_local); dict_add_number(dict, "buffer", (long)buffer_local);
dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L); dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L);
dict_add_string(dict, "mode", mapmode); dict_add_string(dict, "mode", mapmode);
dict_add_number(dict, "abbr", abbr ? 1L : 0L);
vim_free(mapmode); vim_free(mapmode);
} }
@ -2381,7 +2383,8 @@ get_maparg(typval_T *argvars, typval_T *rettv, int exact)
} }
else if (rettv_dict_alloc(rettv) != FAIL && rhs != NULL) else if (rettv_dict_alloc(rettv) != FAIL && rhs != NULL)
mapblock2dict(mp, rettv->vval.v_dict, mapblock2dict(mp, rettv->vval.v_dict,
did_simplify ? keys_simplified : NULL, buffer_local); did_simplify ? keys_simplified : NULL,
buffer_local, abbr);
vim_free(keys_buf); vim_free(keys_buf);
vim_free(alt_keys_buf); vim_free(alt_keys_buf);
@ -2448,7 +2451,8 @@ f_maplist(typval_T *argvars UNUSED, typval_T *rettv)
vim_free(lhs); vim_free(lhs);
mapblock2dict(mp, d, mapblock2dict(mp, d,
did_simplify ? keys_buf : NULL, buffer_local); did_simplify ? keys_buf : NULL,
buffer_local, abbr);
vim_free(keys_buf); vim_free(keys_buf);
} }
} }
@ -2489,6 +2493,56 @@ f_mapcheck(typval_T *argvars, typval_T *rettv)
get_maparg(argvars, rettv, FALSE); get_maparg(argvars, rettv, FALSE);
} }
/*
* Get the mapping mode from the mode string.
* It may contain multiple characters, eg "nox", or "!", or ' '
* Return 0 if there is an error.
*/
static int
get_map_mode_string(char_u *mode_string, int abbr)
{
char_u *p = mode_string;
int mode = 0;
int tmode;
int modec;
const int MASK_V = VISUAL + SELECTMODE;
const int MASK_MAP = VISUAL + SELECTMODE + NORMAL + OP_PENDING;
const int MASK_BANG = INSERT + CMDLINE;
if (*p == NUL)
p = (char_u *)" "; // compatibility
while ((modec = *p++))
{
switch (modec)
{
case 'i': tmode = INSERT; break;
case 'l': tmode = LANGMAP; break;
case 'c': tmode = CMDLINE; break;
case 'n': tmode = NORMAL; break;
case 'x': tmode = VISUAL; break;
case 's': tmode = SELECTMODE; break;
case 'o': tmode = OP_PENDING; break;
case 't': tmode = TERMINAL; break;
case 'v': tmode = MASK_V; break;
case '!': tmode = MASK_BANG; break;
case ' ': tmode = MASK_MAP; break;
default:
return 0; // error, unknown mode character
}
mode |= tmode;
}
if ((abbr && (mode & ~MASK_BANG) != 0)
|| (!abbr && (mode & (mode-1)) != 0 // more than one bit set
&& (
// false if multiple bits set in mode and mode is fully
// contained in one mask
!(((mode & MASK_BANG) != 0 && (mode & ~MASK_BANG) == 0)
|| ((mode & MASK_MAP) != 0 && (mode & ~MASK_MAP) == 0)))))
return 0;
return mode;
}
/* /*
* "mapset()" function * "mapset()" function
*/ */
@ -2518,25 +2572,51 @@ f_mapset(typval_T *argvars, typval_T *rettv UNUSED)
mapblock_T **abbr_table = &first_abbr; mapblock_T **abbr_table = &first_abbr;
int nowait; int nowait;
char_u *arg; char_u *arg;
int dict_only;
// If first arg is a dict, then that's the only arg permitted.
dict_only = argvars[0].v_type == VAR_DICT;
if (in_vim9script() if (in_vim9script()
&& (check_for_string_or_dict_arg(argvars, 0) == FAIL
|| (dict_only && check_for_unknown_arg(argvars, 1) == FAIL)
|| (!dict_only
&& (check_for_string_arg(argvars, 0) == FAIL && (check_for_string_arg(argvars, 0) == FAIL
|| check_for_bool_arg(argvars, 1) == FAIL || check_for_bool_arg(argvars, 1) == FAIL
|| check_for_dict_arg(argvars, 2) == FAIL)) || check_for_dict_arg(argvars, 2) == FAIL))))
return; return;
if (dict_only)
{
d = argvars[0].vval.v_dict;
which = dict_get_string(d, (char_u *)"mode", FALSE);
is_abbr = dict_get_bool(d, (char_u *)"abbr", -1);
if (which == NULL || is_abbr < 0)
{
emsg(_(e_entries_missing_in_mapset_dict_argument));
return;
}
}
else
{
which = tv_get_string_buf_chk(&argvars[0], buf); which = tv_get_string_buf_chk(&argvars[0], buf);
if (which == NULL) if (which == NULL)
return; return;
mode = get_map_mode(&which, 0);
is_abbr = (int)tv_get_bool(&argvars[1]); is_abbr = (int)tv_get_bool(&argvars[1]);
if (argvars[2].v_type != VAR_DICT) if (argvars[2].v_type != VAR_DICT)
{ {
emsg(_(e_key_not_present_in_dictionary)); emsg(_(e_dictionary_required));
return; return;
} }
d = argvars[2].vval.v_dict; d = argvars[2].vval.v_dict;
}
mode = get_map_mode_string(which, is_abbr);
if (mode == 0)
{
semsg(_(e_illegal_map_mode_string_str), which);
return;
}
// Get the values in the same order as above in get_maparg(). // Get the values in the same order as above in get_maparg().
lhs = dict_get_string(d, (char_u *)"lhs", FALSE); lhs = dict_get_string(d, (char_u *)"lhs", FALSE);

View File

@ -9,6 +9,7 @@ varnumber_T tv_get_number_chk(typval_T *varp, int *denote);
varnumber_T tv_get_bool(typval_T *varp); varnumber_T tv_get_bool(typval_T *varp);
varnumber_T tv_get_bool_chk(typval_T *varp, int *denote); varnumber_T tv_get_bool_chk(typval_T *varp, int *denote);
float_T tv_get_float(typval_T *varp); float_T tv_get_float(typval_T *varp);
int check_for_unknown_arg(typval_T *args, int idx);
int check_for_string_arg(typval_T *args, int idx); int check_for_string_arg(typval_T *args, int idx);
int check_for_nonempty_string_arg(typval_T *args, int idx); int check_for_nonempty_string_arg(typval_T *args, int idx);
int check_for_opt_string_arg(typval_T *args, int idx); int check_for_opt_string_arg(typval_T *args, int idx);

View File

@ -19,13 +19,13 @@ func Test_maparg()
\ 'lhsraw': "foo\x80\xfc\x04V", 'lhsrawalt': "foo\x16", \ 'lhsraw': "foo\x80\xfc\x04V", 'lhsrawalt': "foo\x16",
\ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1, \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1,
\ 'lnum': lnum + 1, \ 'lnum': lnum + 1,
\ 'rhs': 'is<F4>foo', 'buffer': 0}, \ 'rhs': 'is<F4>foo', 'buffer': 0, 'abbr': 0},
\ maparg('foo<C-V>', '', 0, 1)) \ maparg('foo<C-V>', '', 0, 1))
call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar', call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar',
\ 'lhsraw': 'bar', 'mode': 'v', \ 'lhsraw': 'bar', 'mode': 'v',
\ 'nowait': 0, 'expr': 1, 'sid': sid, 'scriptversion': 1, \ 'nowait': 0, 'expr': 1, 'sid': sid, 'scriptversion': 1,
\ 'lnum': lnum + 2, \ 'lnum': lnum + 2,
\ 'rhs': 'isbar', 'buffer': 1}, \ 'rhs': 'isbar', 'buffer': 1, 'abbr': 0},
\ 'bar'->maparg('', 0, 1)) \ 'bar'->maparg('', 0, 1))
let lnum = expand('<sflnum>') let lnum = expand('<sflnum>')
map <buffer> <nowait> foo bar map <buffer> <nowait> foo bar
@ -33,7 +33,7 @@ func Test_maparg()
\ 'lhsraw': 'foo', 'mode': ' ', \ 'lhsraw': 'foo', 'mode': ' ',
\ 'nowait': 1, 'expr': 0, 'sid': sid, 'scriptversion': 1, \ 'nowait': 1, 'expr': 0, 'sid': sid, 'scriptversion': 1,
\ 'lnum': lnum + 1, 'rhs': 'bar', \ 'lnum': lnum + 1, 'rhs': 'bar',
\ 'buffer': 1}, \ 'buffer': 1, 'abbr': 0},
\ maparg('foo', '', 0, 1)) \ maparg('foo', '', 0, 1))
let lnum = expand('<sflnum>') let lnum = expand('<sflnum>')
tmap baz foo tmap baz foo
@ -41,8 +41,17 @@ func Test_maparg()
\ 'lhsraw': 'baz', 'mode': 't', \ 'lhsraw': 'baz', 'mode': 't',
\ 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1, \ 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1,
\ 'lnum': lnum + 1, 'rhs': 'foo', \ 'lnum': lnum + 1, 'rhs': 'foo',
\ 'buffer': 0}, \ 'buffer': 0, 'abbr': 0},
\ maparg('baz', 't', 0, 1)) \ maparg('baz', 't', 0, 1))
let lnum = expand('<sflnum>')
iab A B
call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'A',
\ 'lhsraw': 'A', 'mode': 'i',
\ 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1,
\ 'lnum': lnum + 1, 'rhs': 'B',
\ 'buffer': 0, 'abbr': 1},
\ maparg('A', 'i', 1, 1))
iuna A
map abc x<char-114>x map abc x<char-114>x
call assert_equal("xrx", maparg('abc')) call assert_equal("xrx", maparg('abc'))
@ -250,10 +259,156 @@ func Test_mapset()
bwipe! bwipe!
call assert_fails('call mapset([], v:false, {})', 'E730:') call assert_fails('call mapset([], v:false, {})', 'E730:')
call assert_fails('call mapset("i", 0, "")', 'E716:') call assert_fails('call mapset("i", 0, "")', 'E715:')
call assert_fails('call mapset("i", 0, {})', 'E460:') call assert_fails('call mapset("i", 0, {})', 'E460:')
endfunc endfunc
def Test_mapset_arg1_dir()
# This test is mostly about get_map_mode_string.
# Once the code gets past that, it's common with the 3 arg mapset.
# GetModes() return list of modes for 'XZ' lhs using maplist.
# There is one list item per mapping
def GetModes(abbr: bool = false): list<string>
return maplist(abbr)->filter((_, m) => m.lhs == 'XZ')
->mapnew((_, m) => m.mode)
enddef
const unmap_cmds = [ 'unmap', 'unmap!', 'tunmap', 'lunmap' ]
def UnmapAll(lhs: string)
for cmd in unmap_cmds
try | execute(cmd .. ' ' .. lhs) | catch /E31/ | endtry
endfor
enddef
var tmap: dict<any>
# some mapset(mode, abbr, dict) tests using get_map_mode_str
map XZ x
tmap = maplist()->filter((_, m) => m.lhs == 'XZ')[0]->copy()
# this splits the mapping into 2 mappings
mapset('ox', false, tmap)
assert_equal(2, len(GetModes()))
mapset('o', false, tmap)
assert_equal(3, len(GetModes()))
# test that '' acts like ' ', and that the 3 mappings become 1
mapset('', false, tmap)
assert_equal([' '], GetModes())
# dict's mode/abbr are ignored
UnmapAll('XZ')
tmap.mode = '!'
tmap.abbr = true
mapset('o', false, tmap)
assert_equal(['o'], GetModes())
# test the 3 arg version handles bad mode string, dict not used
assert_fails("mapset('vi', false, {})", 'E1276:')
# get the abbreviations out of the way
abbreviate XZ ZX
tmap = maplist(true)->filter((_, m) => m.lhs == 'XZ')[0]->copy()
abclear
# 'ic' is the default ab command, shows up as '!'
tmap.mode = 'ic'
mapset(tmap)
assert_equal(['!'], GetModes(true))
abclear
tmap.mode = 'i'
mapset(tmap)
assert_equal(['i'], GetModes(true))
abclear
tmap.mode = 'c'
mapset(tmap)
assert_equal(['c'], GetModes(true))
abclear
tmap.mode = '!'
mapset(tmap)
assert_equal(['!'], GetModes(true))
assert_fails("mapset({mode: ' !', abbr: 1})", 'E1276:')
assert_fails("mapset({mode: 'cl', abbr: 1})", 'E1276:')
assert_fails("mapset({mode: 'in', abbr: 1})", 'E1276:')
# the map commands
map XZ x
tmap = maplist()->filter((_, m) => m.lhs == 'XZ')[0]->copy()
# try the combos
UnmapAll('XZ')
# 'nxso' is ' ', the unadorned :map
tmap.mode = 'nxso'
mapset(tmap)
assert_equal([' '], GetModes())
UnmapAll('XZ')
# 'ic' is '!'
tmap.mode = 'ic'
mapset(tmap)
assert_equal(['!'], GetModes())
UnmapAll('XZ')
# 'xs' is really 'v'
tmap.mode = 'xs'
mapset(tmap)
assert_equal(['v'], GetModes())
# try the individual modes
UnmapAll('XZ')
tmap.mode = 'n'
mapset(tmap)
assert_equal(['n'], GetModes())
UnmapAll('XZ')
tmap.mode = 'x'
mapset(tmap)
assert_equal(['x'], GetModes())
UnmapAll('XZ')
tmap.mode = 's'
mapset(tmap)
assert_equal(['s'], GetModes())
UnmapAll('XZ')
tmap.mode = 'o'
mapset(tmap)
assert_equal(['o'], GetModes())
UnmapAll('XZ')
tmap.mode = 'i'
mapset(tmap)
assert_equal(['i'], GetModes())
UnmapAll('XZ')
tmap.mode = 'c'
mapset(tmap)
assert_equal(['c'], GetModes())
UnmapAll('XZ')
tmap.mode = 't'
mapset(tmap)
assert_equal(['t'], GetModes())
UnmapAll('XZ')
tmap.mode = 'l'
mapset(tmap)
assert_equal(['l'], GetModes())
UnmapAll('XZ')
# get errors for modes that can't be in one mapping
assert_fails("mapset({mode: 'nxsoi', abbr: 0})", 'E1276:')
assert_fails("mapset({mode: ' !', abbr: 0})", 'E1276:')
assert_fails("mapset({mode: 'ix', abbr: 0})", 'E1276:')
assert_fails("mapset({mode: 'tl', abbr: 0})", 'E1276:')
assert_fails("mapset({mode: ' l', abbr: 0})", 'E1276:')
assert_fails("mapset({mode: ' t', abbr: 0})", 'E1276:')
enddef
func Check_ctrlb_map(d, check_alt) func Check_ctrlb_map(d, check_alt)
call assert_equal('<C-B>', a:d.lhs) call assert_equal('<C-B>', a:d.lhs)
if a:check_alt if a:check_alt

View File

@ -2463,7 +2463,8 @@ def Test_maparg()
sid: SID(), sid: SID(),
scriptversion: 999999, scriptversion: 999999,
rhs: 'bar', rhs: 'bar',
buffer: 0}) buffer: 0,
abbr: 0})
unmap foo unmap foo
v9.CheckDefAndScriptFailure(['maparg(1)'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1']) v9.CheckDefAndScriptFailure(['maparg(1)'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1'])
v9.CheckDefAndScriptFailure(['maparg("a", 2)'], ['E1013: Argument 2: type mismatch, expected string but got number', 'E1174: String required for argument 2']) v9.CheckDefAndScriptFailure(['maparg("a", 2)'], ['E1013: Argument 2: type mismatch, expected string but got number', 'E1174: String required for argument 2'])
@ -2545,7 +2546,7 @@ def Test_mapnew()
enddef enddef
def Test_mapset() def Test_mapset()
v9.CheckDefAndScriptFailure(['mapset(1, true, {})'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1']) v9.CheckDefAndScriptFailure(['mapset(1, true, {})'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1223: String or Dictionary required for argument 1'])
v9.CheckDefAndScriptFailure(['mapset("a", 2, {})'], ['E1013: Argument 2: type mismatch, expected bool but got number', 'E1212: Bool required for argument 2']) v9.CheckDefAndScriptFailure(['mapset("a", 2, {})'], ['E1013: Argument 2: type mismatch, expected bool but got number', 'E1212: Bool required for argument 2'])
v9.CheckDefAndScriptFailure(['mapset("a", false, [])'], ['E1013: Argument 3: type mismatch, expected dict<any> but got list<unknown>', 'E1206: Dictionary required for argument 3']) v9.CheckDefAndScriptFailure(['mapset("a", false, [])'], ['E1013: Argument 3: type mismatch, expected dict<any> but got list<unknown>', 'E1206: Dictionary required for argument 3'])
enddef enddef

View File

@ -359,6 +359,20 @@ tv_get_float(typval_T *varp)
} }
#endif #endif
/*
* Give an error and return FAIL unless "args[idx]" is unknown
*/
int
check_for_unknown_arg(typval_T *args, int idx)
{
if (args[idx].v_type != VAR_UNKNOWN)
{
semsg(_(e_too_many_arguments), idx + 1);
return FAIL;
}
return OK;
}
/* /*
* Give an error and return FAIL unless "args[idx]" is a string. * Give an error and return FAIL unless "args[idx]" is a string.
*/ */

View File

@ -746,6 +746,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 */
/**/
4861,
/**/ /**/
4860, 4860,
/**/ /**/