Skip to content

Commit 51d04d1

Browse files
erraelbrammool
authored andcommitted
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)
1 parent 05cf63e commit 51d04d1

File tree

9 files changed

+322
-28
lines changed

9 files changed

+322
-28
lines changed

runtime/doc/builtin.txt

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5310,6 +5310,7 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
53105310
"lnum" The line number in "sid", zero if unknown.
53115311
"nowait" Do not wait for other, longer mappings.
53125312
(|:map-<nowait>|).
5313+
"abbr" True if this is an |abbreviation|.
53135314

53145315
The dictionary can be used to restore a mapping with
53155316
|mapset()|.
@@ -5380,9 +5381,18 @@ mapnew({expr1}, {expr2}) *mapnew()*
53805381

53815382

53825383
mapset({mode}, {abbr}, {dict}) *mapset()*
5383-
Restore a mapping from a dictionary returned by |maparg()|.
5384-
{mode} and {abbr} should be the same as for the call to
5385-
|maparg()|. *E460*
5384+
mapset({dict})
5385+
Restore a mapping from a dictionary, possibly returned by
5386+
|maparg()| or |maplist()|. A buffer mapping, when dict.buffer
5387+
is true, is set on the current buffer; it is up to the caller
5388+
to insure that the intended buffer is the current buffer. This
5389+
feature allows copying mappings from one buffer to another.
5390+
The dict.mode value may restore a single mapping that covers
5391+
more than one mode, like with mode values of '!', ' ', 'nox',
5392+
or 'v'. *E1276*
5393+
5394+
In the first form, {mode} and {abbr} should be the same as
5395+
for the call to |maparg()|. *E460*
53865396
{mode} is used to define the mode in which the mapping is set,
53875397
not the "mode" entry in {dict}.
53885398
Example for saving and restoring a mapping: >
@@ -5391,8 +5401,22 @@ mapset({mode}, {abbr}, {dict}) *mapset()*
53915401
...
53925402
call mapset('n', 0, save_map)
53935403
< Note that if you are going to replace a map in several modes,
5394-
e.g. with `:map!`, you need to save the mapping for all of
5395-
them, since they can differ.
5404+
e.g. with `:map!`, you need to save/restore the mapping for
5405+
all of them, when they might differ.
5406+
5407+
In the second form, with {dict} as the only argument, mode
5408+
and abbr are taken from the dict.
5409+
Example: >
5410+
vim9script
5411+
var save_maps = maplist()->filter(
5412+
(_, m) => m.lhs == 'K')
5413+
nnoremap K somethingelse
5414+
cnoremap K somethingelse2
5415+
# ...
5416+
unmap K
5417+
for d in save_maps
5418+
mapset(d)
5419+
endfor
53965420
53975421
53985422
match({expr}, {pat} [, {start} [, {count}]]) *match()*

src/errors.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3262,4 +3262,6 @@ EXTERN char e_no_script_file_name_to_substitute_for_script[]
32623262
#ifdef FEAT_EVAL
32633263
EXTERN char e_string_or_function_required_for_arrow_parens_expr[]
32643264
INIT(= N_("E1275: String or function required for ->(expr)"));
3265+
EXTERN char e_illegal_map_mode_string_str[]
3266+
INIT(= N_("E1276: Illegal map mode string: '%s'"));
32653267
#endif

src/evalfunc.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,21 @@ arg_string_or_list_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *con
421421
return FAIL;
422422
}
423423

424+
/*
425+
* Check "type" is a string or a dict of 'any'
426+
*/
427+
static int
428+
arg_string_or_dict_any(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
429+
{
430+
if (type->tt_type == VAR_ANY
431+
|| type->tt_type == VAR_UNKNOWN
432+
|| type->tt_type == VAR_STRING
433+
|| type->tt_type == VAR_DICT)
434+
return OK;
435+
arg_type_mismatch(&t_string, type, context->arg_idx + 1);
436+
return FAIL;
437+
}
438+
424439
/*
425440
* Check "type" is a string or a blob
426441
*/
@@ -998,8 +1013,8 @@ static argcheck_T arg3_string[] = {arg_string, arg_string, arg_string};
9981013
static argcheck_T arg3_string_any_dict[] = {arg_string, NULL, arg_dict_any};
9991014
static argcheck_T arg3_string_any_string[] = {arg_string, NULL, arg_string};
10001015
static argcheck_T arg3_string_bool_bool[] = {arg_string, arg_bool, arg_bool};
1001-
static argcheck_T arg3_string_bool_dict[] = {arg_string, arg_bool, arg_dict_any};
10021016
static argcheck_T arg3_string_number_bool[] = {arg_string, arg_number, arg_bool};
1017+
static argcheck_T arg3_string_or_dict_bool_dict[] = {arg_string_or_dict_any, arg_bool, arg_dict_any};
10031018
static argcheck_T arg3_string_string_bool[] = {arg_string, arg_string, arg_bool};
10041019
static argcheck_T arg3_string_string_dict[] = {arg_string, arg_string, arg_dict_any};
10051020
static argcheck_T arg3_string_string_number[] = {arg_string, arg_string, arg_number};
@@ -2053,7 +2068,7 @@ static funcentry_T global_functions[] =
20532068
ret_list_dict_any, f_maplist},
20542069
{"mapnew", 2, 2, FEARG_1, arg2_mapnew,
20552070
ret_first_cont, f_mapnew},
2056-
{"mapset", 3, 3, FEARG_1, arg3_string_bool_dict,
2071+
{"mapset", 1, 3, FEARG_1, arg3_string_or_dict_bool_dict,
20572072
ret_void, f_mapset},
20582073
{"match", 2, 4, FEARG_1, arg24_match_func,
20592074
ret_any, f_match},

src/map.c

Lines changed: 94 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2283,7 +2283,8 @@ mapblock2dict(
22832283
mapblock_T *mp,
22842284
dict_T *dict,
22852285
char_u *lhsrawalt, // may be NULL
2286-
int buffer_local) // false if not buffer local mapping
2286+
int buffer_local, // false if not buffer local mapping
2287+
int abbr) // true if abbreviation
22872288
{
22882289
char_u *lhs = str2special_save(mp->m_keys, TRUE);
22892290
char_u *mapmode = map_mode_to_chars(mp->m_mode);
@@ -2307,6 +2308,7 @@ mapblock2dict(
23072308
dict_add_number(dict, "buffer", (long)buffer_local);
23082309
dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L);
23092310
dict_add_string(dict, "mode", mapmode);
2311+
dict_add_number(dict, "abbr", abbr ? 1L : 0L);
23102312

23112313
vim_free(mapmode);
23122314
}
@@ -2381,7 +2383,8 @@ get_maparg(typval_T *argvars, typval_T *rettv, int exact)
23812383
}
23822384
else if (rettv_dict_alloc(rettv) != FAIL && rhs != NULL)
23832385
mapblock2dict(mp, rettv->vval.v_dict,
2384-
did_simplify ? keys_simplified : NULL, buffer_local);
2386+
did_simplify ? keys_simplified : NULL,
2387+
buffer_local, abbr);
23852388

23862389
vim_free(keys_buf);
23872390
vim_free(alt_keys_buf);
@@ -2448,7 +2451,8 @@ f_maplist(typval_T *argvars UNUSED, typval_T *rettv)
24482451
vim_free(lhs);
24492452

24502453
mapblock2dict(mp, d,
2451-
did_simplify ? keys_buf : NULL, buffer_local);
2454+
did_simplify ? keys_buf : NULL,
2455+
buffer_local, abbr);
24522456
vim_free(keys_buf);
24532457
}
24542458
}
@@ -2489,6 +2493,56 @@ f_mapcheck(typval_T *argvars, typval_T *rettv)
24892493
get_maparg(argvars, rettv, FALSE);
24902494
}
24912495

2496+
/*
2497+
* Get the mapping mode from the mode string.
2498+
* It may contain multiple characters, eg "nox", or "!", or ' '
2499+
* Return 0 if there is an error.
2500+
*/
2501+
static int
2502+
get_map_mode_string(char_u *mode_string, int abbr)
2503+
{
2504+
char_u *p = mode_string;
2505+
int mode = 0;
2506+
int tmode;
2507+
int modec;
2508+
const int MASK_V = VISUAL + SELECTMODE;
2509+
const int MASK_MAP = VISUAL + SELECTMODE + NORMAL + OP_PENDING;
2510+
const int MASK_BANG = INSERT + CMDLINE;
2511+
2512+
if (*p == NUL)
2513+
p = (char_u *)" "; // compatibility
2514+
while ((modec = *p++))
2515+
{
2516+
switch (modec)
2517+
{
2518+
case 'i': tmode = INSERT; break;
2519+
case 'l': tmode = LANGMAP; break;
2520+
case 'c': tmode = CMDLINE; break;
2521+
case 'n': tmode = NORMAL; break;
2522+
case 'x': tmode = VISUAL; break;
2523+
case 's': tmode = SELECTMODE; break;
2524+
case 'o': tmode = OP_PENDING; break;
2525+
case 't': tmode = TERMINAL; break;
2526+
case 'v': tmode = MASK_V; break;
2527+
case '!': tmode = MASK_BANG; break;
2528+
case ' ': tmode = MASK_MAP; break;
2529+
default:
2530+
return 0; // error, unknown mode character
2531+
}
2532+
mode |= tmode;
2533+
}
2534+
if ((abbr && (mode & ~MASK_BANG) != 0)
2535+
|| (!abbr && (mode & (mode-1)) != 0 // more than one bit set
2536+
&& (
2537+
// false if multiple bits set in mode and mode is fully
2538+
// contained in one mask
2539+
!(((mode & MASK_BANG) != 0 && (mode & ~MASK_BANG) == 0)
2540+
|| ((mode & MASK_MAP) != 0 && (mode & ~MASK_MAP) == 0)))))
2541+
return 0;
2542+
2543+
return mode;
2544+
}
2545+
24922546
/*
24932547
* "mapset()" function
24942548
*/
@@ -2518,25 +2572,51 @@ f_mapset(typval_T *argvars, typval_T *rettv UNUSED)
25182572
mapblock_T **abbr_table = &first_abbr;
25192573
int nowait;
25202574
char_u *arg;
2575+
int dict_only;
25212576

2577+
// If first arg is a dict, then that's the only arg permitted.
2578+
dict_only = argvars[0].v_type == VAR_DICT;
25222579
if (in_vim9script()
2523-
&& (check_for_string_arg(argvars, 0) == FAIL
2524-
|| check_for_bool_arg(argvars, 1) == FAIL
2525-
|| check_for_dict_arg(argvars, 2) == FAIL))
2580+
&& (check_for_string_or_dict_arg(argvars, 0) == FAIL
2581+
|| (dict_only && check_for_unknown_arg(argvars, 1) == FAIL)
2582+
|| (!dict_only
2583+
&& (check_for_string_arg(argvars, 0) == FAIL
2584+
|| check_for_bool_arg(argvars, 1) == FAIL
2585+
|| check_for_dict_arg(argvars, 2) == FAIL))))
25262586
return;
25272587

2528-
which = tv_get_string_buf_chk(&argvars[0], buf);
2529-
if (which == NULL)
2530-
return;
2531-
mode = get_map_mode(&which, 0);
2532-
is_abbr = (int)tv_get_bool(&argvars[1]);
2588+
if (dict_only)
2589+
{
2590+
d = argvars[0].vval.v_dict;
2591+
which = dict_get_string(d, (char_u *)"mode", FALSE);
2592+
is_abbr = dict_get_bool(d, (char_u *)"abbr", -1);
2593+
if (which == NULL || is_abbr < 0)
2594+
{
2595+
emsg(_(e_entries_missing_in_mapset_dict_argument));
2596+
return;
2597+
}
2598+
}
2599+
else
2600+
{
2601+
which = tv_get_string_buf_chk(&argvars[0], buf);
2602+
if (which == NULL)
2603+
return;
2604+
is_abbr = (int)tv_get_bool(&argvars[1]);
25332605

2534-
if (argvars[2].v_type != VAR_DICT)
2606+
if (argvars[2].v_type != VAR_DICT)
2607+
{
2608+
emsg(_(e_dictionary_required));
2609+
return;
2610+
}
2611+
d = argvars[2].vval.v_dict;
2612+
}
2613+
mode = get_map_mode_string(which, is_abbr);
2614+
if (mode == 0)
25352615
{
2536-
emsg(_(e_key_not_present_in_dictionary));
2616+
semsg(_(e_illegal_map_mode_string_str), which);
25372617
return;
25382618
}
2539-
d = argvars[2].vval.v_dict;
2619+
25402620

25412621
// Get the values in the same order as above in get_maparg().
25422622
lhs = dict_get_string(d, (char_u *)"lhs", FALSE);

src/proto/typval.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ varnumber_T tv_get_number_chk(typval_T *varp, int *denote);
99
varnumber_T tv_get_bool(typval_T *varp);
1010
varnumber_T tv_get_bool_chk(typval_T *varp, int *denote);
1111
float_T tv_get_float(typval_T *varp);
12+
int check_for_unknown_arg(typval_T *args, int idx);
1213
int check_for_string_arg(typval_T *args, int idx);
1314
int check_for_nonempty_string_arg(typval_T *args, int idx);
1415
int check_for_opt_string_arg(typval_T *args, int idx);

0 commit comments

Comments
 (0)