1
0
forked from aniani/vim

patch 8.2.0807: cannot easily restore a mapping

Problem:    Cannot easily restore a mapping.
Solution:   Add mapset().
This commit is contained in:
Bram Moolenaar
2020-05-22 13:10:44 +02:00
parent 74fae513f8
commit 4c9243f9fb
6 changed files with 231 additions and 61 deletions

View File

@@ -2586,6 +2586,8 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]])
rhs of mapping {name} in mode {mode} rhs of mapping {name} in mode {mode}
mapcheck({name} [, {mode} [, {abbr}]]) mapcheck({name} [, {mode} [, {abbr}]])
String check for mappings matching {name} String check for mappings matching {name}
mapset({name}, {mode}, {abbr}, {dict}
none restore mapping from |maparg()| result
match({expr}, {pat} [, {start} [, {count}]]) match({expr}, {pat} [, {start} [, {count}]])
Number position where {pat} matches in {expr} Number position where {pat} matches in {expr}
matchadd({group}, {pattern} [, {priority} [, {id} [, {dict}]]]) matchadd({group}, {pattern} [, {priority} [, {id} [, {dict}]]])
@@ -6794,6 +6796,7 @@ map({expr1}, {expr2}) *map()*
Can also be used as a |method|: > Can also be used as a |method|: >
mylist->map(expr2) mylist->map(expr2)
maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()* maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
When {dict} is omitted or zero: Return the rhs of mapping When {dict} is omitted or zero: Return the rhs of mapping
{name} in mode {mode}. The returned String has special {name} in mode {mode}. The returned String has special
@@ -6844,6 +6847,10 @@ 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>|).
"simplified"
The dictionary can be used to restore a mapping with
|mapset()|.
The mappings local to the current buffer are checked first, The mappings local to the current buffer are checked first,
then the global mappings. then the global mappings.
@@ -6890,6 +6897,18 @@ mapcheck({name} [, {mode} [, {abbr}]]) *mapcheck()*
Can also be used as a |method|: > Can also be used as a |method|: >
GetKey()->mapcheck('n') GetKey()->mapcheck('n')
mapset({mode}, {abbr}, {dict}) *mapset()*
Restore a mapping from a dictionary returned by |maparg()|.
{name}, {mode} and {abbr} should be the same as for the call
to |maparg()|.
{mode} is used to define the mode in which the mapping is set,
not the "mode" entry in {dict}.
Example for saving and restoring a mapping: >
let save_map = maparg('K', 'n', 0, 1)
nnoremap K somethingelse
...
call mapset('n', 0, save_map)
<
match({expr}, {pat} [, {start} [, {count}]]) *match()* match({expr}, {pat} [, {start} [, {count}]]) *match()*
When {expr} is a |List| then this returns the index of the When {expr} is a |List| then this returns the index of the
first item where {pat} matches. Each item is used as a first item where {pat} matches. Each item is used as a

View File

@@ -664,6 +664,7 @@ static funcentry_T global_functions[] =
{"map", 2, 2, FEARG_1, ret_any, f_map}, {"map", 2, 2, FEARG_1, ret_any, f_map},
{"maparg", 1, 4, FEARG_1, ret_string, f_maparg}, {"maparg", 1, 4, FEARG_1, ret_string, f_maparg},
{"mapcheck", 1, 3, FEARG_1, ret_string, f_mapcheck}, {"mapcheck", 1, 3, FEARG_1, ret_string, f_mapcheck},
{"mapset", 3, 3, FEARG_1, ret_void, f_mapset},
{"match", 2, 4, FEARG_1, ret_any, f_match}, {"match", 2, 4, FEARG_1, ret_any, f_match},
{"matchadd", 2, 5, FEARG_1, ret_number, f_matchadd}, {"matchadd", 2, 5, FEARG_1, ret_number, f_matchadd},
{"matchaddpos", 2, 5, FEARG_1, ret_number, f_matchaddpos}, {"matchaddpos", 2, 5, FEARG_1, ret_number, f_matchaddpos},

222
src/map.c
View File

@@ -204,6 +204,86 @@ showmap(
out_flush(); // show one line at a time out_flush(); // show one line at a time
} }
static int
map_add(
mapblock_T **map_table,
mapblock_T **abbr_table,
char_u *keys,
char_u *rhs,
char_u *orig_rhs,
int expr,
int noremap,
int nowait,
int silent,
int mode,
int is_abbr,
#ifdef FEAT_EVAL
scid_T sid, // -1 to use current_sctx
linenr_T lnum,
#endif
int simplified)
{
mapblock_T *mp = ALLOC_ONE(mapblock_T);
if (mp == NULL)
return FAIL;
// If CTRL-C has been mapped, don't always use it for Interrupting.
if (*keys == Ctrl_C)
{
if (map_table == curbuf->b_maphash)
curbuf->b_mapped_ctrl_c |= mode;
else
mapped_ctrl_c |= mode;
}
mp->m_keys = vim_strsave(keys);
mp->m_str = vim_strsave(rhs);
mp->m_orig_str = vim_strsave(orig_rhs);
if (mp->m_keys == NULL || mp->m_str == NULL)
{
vim_free(mp->m_keys);
vim_free(mp->m_str);
vim_free(mp->m_orig_str);
vim_free(mp);
return FAIL;
}
mp->m_keylen = (int)STRLEN(mp->m_keys);
mp->m_noremap = noremap;
mp->m_nowait = nowait;
mp->m_silent = silent;
mp->m_mode = mode;
mp->m_simplified = simplified;
#ifdef FEAT_EVAL
mp->m_expr = expr;
if (sid >= 0)
{
mp->m_script_ctx.sc_sid = sid;
mp->m_script_ctx.sc_lnum = lnum;
}
else
{
mp->m_script_ctx = current_sctx;
mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
}
#endif
// add the new entry in front of the abbrlist or maphash[] list
if (is_abbr)
{
mp->m_next = *abbr_table;
*abbr_table = mp;
}
else
{
int n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
mp->m_next = map_table[n];
map_table[n] = mp;
}
return OK;
}
/* /*
* map[!] : show all key mappings * map[!] : show all key mappings
* map[!] {lhs} : show key mapping for {lhs} * map[!] {lhs} : show key mapping for {lhs}
@@ -501,7 +581,8 @@ do_map(
msg_start(); msg_start();
// Check if a new local mapping wasn't already defined globally. // Check if a new local mapping wasn't already defined globally.
if (map_table == curbuf->b_maphash && haskey && hasarg && maptype != 1) if (unique && map_table == curbuf->b_maphash
&& haskey && hasarg && maptype != 1)
{ {
// need to loop over all global hash lists // need to loop over all global hash lists
for (hash = 0; hash < 256 && !got_int; ++hash) for (hash = 0; hash < 256 && !got_int; ++hash)
@@ -519,7 +600,6 @@ do_map(
// check entries with the same mode // check entries with the same mode
if ((mp->m_mode & mode) != 0 if ((mp->m_mode & mode) != 0
&& mp->m_keylen == len && mp->m_keylen == len
&& unique
&& STRNCMP(mp->m_keys, keys, (size_t)len) == 0) && STRNCMP(mp->m_keys, keys, (size_t)len) == 0)
{ {
if (abbrev) if (abbrev)
@@ -759,58 +839,17 @@ do_map(
continue; // have added the new entry already continue; // have added the new entry already
// Get here when adding a new entry to the maphash[] list or abbrlist. // Get here when adding a new entry to the maphash[] list or abbrlist.
mp = ALLOC_ONE(mapblock_T); if (map_add(map_table, abbr_table, keys, rhs, orig_rhs, expr,
if (mp == NULL) noremap, nowait, silent, mode,
abbrev,
#ifdef FEAT_EVAL
/* sid */ -1, /* lnum */ 0,
#endif
did_simplify && keyround == 1) == FAIL)
{ {
retval = 4; // no mem retval = 4; // no mem
goto theend; goto theend;
} }
// If CTRL-C has been mapped, don't always use it for Interrupting.
if (*keys == Ctrl_C)
{
if (map_table == curbuf->b_maphash)
curbuf->b_mapped_ctrl_c |= mode;
else
mapped_ctrl_c |= mode;
}
mp->m_keys = vim_strsave(keys);
mp->m_str = vim_strsave(rhs);
mp->m_orig_str = vim_strsave(orig_rhs);
if (mp->m_keys == NULL || mp->m_str == NULL)
{
vim_free(mp->m_keys);
vim_free(mp->m_str);
vim_free(mp->m_orig_str);
vim_free(mp);
retval = 4; // no mem
goto theend;
}
mp->m_keylen = (int)STRLEN(mp->m_keys);
mp->m_noremap = noremap;
mp->m_nowait = nowait;
mp->m_silent = silent;
mp->m_mode = mode;
mp->m_simplified = did_simplify && keyround == 1;
#ifdef FEAT_EVAL
mp->m_expr = expr;
mp->m_script_ctx = current_sctx;
mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
#endif
// add the new entry in front of the abbrlist or maphash[] list
if (abbrev)
{
mp->m_next = *abbr_table;
*abbr_table = mp;
}
else
{
n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
mp->m_next = map_table[n];
map_table[n] = mp;
}
} }
theend: theend:
@@ -2209,13 +2248,96 @@ get_maparg(typval_T *argvars, typval_T *rettv, int exact)
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, "simplified", mp->m_simplified);
vim_free(lhs); vim_free(lhs);
vim_free(mapmode); vim_free(mapmode);
} }
} }
/*
* "mapset()" function
*/
void
f_mapset(typval_T *argvars, typval_T *rettv UNUSED)
{
char_u *keys;
char_u *keys_buf = NULL;
char_u *which;
int mode;
char_u buf[NUMBUFLEN];
int is_abbr;
dict_T *d;
char_u *lhs;
char_u *rhs;
int noremap;
int expr;
int silent;
scid_T sid;
linenr_T lnum;
mapblock_T **map_table = maphash;
mapblock_T **abbr_table = &first_abbr;
int nowait;
int simplified;
char_u *arg;
which = tv_get_string_buf_chk(&argvars[0], buf);
mode = get_map_mode(&which, 0);
is_abbr = (int)tv_get_number(&argvars[1]);
if (argvars[2].v_type != VAR_DICT)
{
emsg(_(e_dictkey));
return;
}
d = argvars[2].vval.v_dict;
// Get the values in the same order as above in get_maparg().
lhs = dict_get_string(d, (char_u *)"lhs", FALSE);
if (lhs == NULL)
{
emsg(_("E99: lhs entry missing in mapset() dict argument"));
return;
}
rhs = dict_get_string(d, (char_u *)"rhs", FALSE);
if (rhs == NULL)
{
emsg(_("E99: rhs entry missing in mapset() dict argument"));
return;
}
noremap = dict_get_number(d, (char_u *)"noremap") ? REMAP_NONE: 0;
if (dict_get_number(d, (char_u *)"script") != 0)
noremap = REMAP_SCRIPT;
expr = dict_get_number(d, (char_u *)"expr") != 0;
silent = dict_get_number(d, (char_u *)"silent") != 0;
sid = dict_get_number(d, (char_u *)"sid");
lnum = dict_get_number(d, (char_u *)"lnum");
if (dict_get_number(d, (char_u *)"buffer"))
{
map_table = curbuf->b_maphash;
abbr_table = &curbuf->b_first_abbr;
}
nowait = dict_get_number(d, (char_u *)"nowait") != 0;
// mode from the dict is not used
simplified = dict_get_number(d, (char_u *)"simplified") != 0;
// Delete any existing mapping for this lhs and mode.
arg = vim_strsave(lhs);
if (arg == NULL)
return;
do_map(1, arg, mode, is_abbr);
vim_free(arg);
keys = replace_termcodes(lhs, &keys_buf,
REPTERM_FROM_PART | REPTERM_DO_LT, NULL);
(void)map_add(map_table, abbr_table, keys, rhs, rhs, expr,
noremap, nowait, silent, mode, is_abbr, sid, lnum, simplified);
vim_free(keys_buf);
}
#endif #endif
#if defined(MSWIN) || defined(MACOS_X) #if defined(MSWIN) || defined(MACOS_X)
# define VIS_SEL (VISUAL+SELECTMODE) // abbreviation # define VIS_SEL (VISUAL+SELECTMODE) // abbreviation

View File

@@ -18,6 +18,7 @@ int put_escstr(FILE *fd, char_u *strstart, int what);
void check_map_keycodes(void); void check_map_keycodes(void);
char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapblock_T **mp_ptr, int *local_ptr); char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapblock_T **mp_ptr, int *local_ptr);
void get_maparg(typval_T *argvars, typval_T *rettv, int exact); void get_maparg(typval_T *argvars, typval_T *rettv, int exact);
void f_mapset(typval_T *argvars, typval_T *rettv);
void init_mappings(void); void init_mappings(void);
void add_map(char_u *map, int mode); void add_map(char_u *map, int mode);
int langmap_adjust_mb(int c); int langmap_adjust_mb(int c);

View File

@@ -1,12 +1,12 @@
" Tests for maparg(). " Tests for maparg(), mapcheck() and mapset().
" Also test utf8 map with a 0x80 byte. " Also test utf8 map with a 0x80 byte.
" Also test mapcheck() " Also test mapcheck()
function s:SID() func s:SID()
return str2nr(matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')) return str2nr(matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$'))
endfun endfunc
function Test_maparg() funct Test_maparg()
new new
set cpo-=< set cpo-=<
set encoding=utf8 set encoding=utf8
@@ -18,23 +18,23 @@ function Test_maparg()
call assert_equal("is<F4>foo", maparg('foo<C-V>')) call assert_equal("is<F4>foo", maparg('foo<C-V>'))
call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo<C-V>', call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo<C-V>',
\ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1,
\ 'rhs': 'is<F4>foo', 'buffer': 0}, \ 'simplified': 1, 'rhs': 'is<F4>foo', 'buffer': 0},
\ maparg('foo<C-V>', '', 0, 1)) \ maparg('foo<C-V>', '', 0, 1))
call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar', 'mode': 'v', call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar', 'mode': 'v',
\ 'nowait': 0, 'expr': 1, 'sid': sid, 'lnum': lnum + 2, \ 'nowait': 0, 'expr': 1, 'sid': sid, 'lnum': lnum + 2,
\ 'rhs': 'isbar', 'buffer': 1}, \ 'simplified': 0, 'rhs': 'isbar', 'buffer': 1},
\ '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
call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo', 'mode': ' ', call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo', 'mode': ' ',
\ 'nowait': 1, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'bar', \ 'nowait': 1, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'bar',
\ 'buffer': 1}, \ 'simplified': 0, 'buffer': 1},
\ maparg('foo', '', 0, 1)) \ maparg('foo', '', 0, 1))
let lnum = expand('<sflnum>') let lnum = expand('<sflnum>')
tmap baz foo tmap baz foo
call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'baz', 'mode': 't', call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'baz', 'mode': 't',
\ 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'foo', \ 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'foo',
\ 'buffer': 0}, \ 'simplified': 0, 'buffer': 0},
\ maparg('baz', 't', 0, 1)) \ maparg('baz', 't', 0, 1))
map abc x<char-114>x map abc x<char-114>x
@@ -75,7 +75,7 @@ function Test_maparg()
let d = maparg('esc', 'i', 1, 1) let d = maparg('esc', 'i', 1, 1)
call assert_equal(['esc', "\<C-V>\<C-V>\<Esc>", '!'], [d.lhs, d.rhs, d.mode]) call assert_equal(['esc', "\<C-V>\<C-V>\<Esc>", '!'], [d.lhs, d.rhs, d.mode])
abclear abclear
endfunction endfunc
func Test_mapcheck() func Test_mapcheck()
call assert_equal('', mapcheck('a')) call assert_equal('', mapcheck('a'))
@@ -116,7 +116,7 @@ func Test_mapcheck()
unabbr ab unabbr ab
endfunc endfunc
function Test_range_map() func Test_range_map()
new new
" Outside of the range, minimum " Outside of the range, minimum
inoremap <Char-0x1040> a inoremap <Char-0x1040> a
@@ -131,6 +131,31 @@ function Test_range_map()
inoremap <Char-0xf040> d inoremap <Char-0xf040> d
execute "normal a\uf040\<Esc>" execute "normal a\uf040\<Esc>"
call assert_equal("abcd", getline(1)) call assert_equal("abcd", getline(1))
endfunction endfunc
func One_mapset_test(keys)
exe 'nnoremap ' .. a:keys .. ' original<CR>'
let orig = maparg(a:keys, 'n', 0, 1)
call assert_equal(a:keys, orig.lhs)
call assert_equal('original<CR>', orig.rhs)
call assert_equal('n', orig.mode)
exe 'nunmap ' .. a:keys
let d = maparg(a:keys, 'n', 0, 1)
call assert_equal({}, d)
call mapset('n', 0, orig)
let d = maparg(a:keys, 'n', 0, 1)
call assert_equal(a:keys, d.lhs)
call assert_equal('original<CR>', d.rhs)
call assert_equal('n', d.mode)
exe 'nunmap ' .. a:keys
endfunc
func Test_mapset()
call One_mapset_test('K')
call One_mapset_test('<F3>')
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

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 */
/**/
807,
/**/ /**/
806, 806,
/**/ /**/