# HG changeset patch # User Bram Moolenaar # Date 1590146104 -7200 # Node ID aee0b72ca6d6d0e4370b0d6f065fcb736b933584 # Parent af5d9cf7e7a1942a5cc0f4b3bf281dcf6f0faba1 patch 8.2.0807: cannot easily restore a mapping Commit: https://github.com/vim/vim/commit/4c9243f9fb708c9010867d3cc8e928f36b58509a Author: Bram Moolenaar Date: Fri May 22 13:10:44 2020 +0200 patch 8.2.0807: cannot easily restore a mapping Problem: Cannot easily restore a mapping. Solution: Add mapset(). diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2586,6 +2586,8 @@ maparg({name} [, {mode} [, {abbr} [, {di rhs of mapping {name} in mode {mode} mapcheck({name} [, {mode} [, {abbr}]]) String check for mappings matching {name} +mapset({name}, {mode}, {abbr}, {dict} + none restore mapping from |maparg()| result match({expr}, {pat} [, {start} [, {count}]]) Number position where {pat} matches in {expr} matchadd({group}, {pattern} [, {priority} [, {id} [, {dict}]]]) @@ -6794,6 +6796,7 @@ map({expr1}, {expr2}) *map()* Can also be used as a |method|: > mylist->map(expr2) + maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()* When {dict} is omitted or zero: Return the rhs of mapping {name} in mode {mode}. The returned String has special @@ -6844,6 +6847,10 @@ maparg({name} [, {mode} [, {abbr} [, {di "lnum" The line number in "sid", zero if unknown. "nowait" Do not wait for other, longer mappings. (|:map-|). + "simplified" + + The dictionary can be used to restore a mapping with + |mapset()|. The mappings local to the current buffer are checked first, then the global mappings. @@ -6890,6 +6897,18 @@ mapcheck({name} [, {mode} [, {abbr}]]) Can also be used as a |method|: > 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()* When {expr} is a |List| then this returns the index of the first item where {pat} matches. Each item is used as a diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -664,6 +664,7 @@ static funcentry_T global_functions[] = {"map", 2, 2, FEARG_1, ret_any, f_map}, {"maparg", 1, 4, FEARG_1, ret_string, f_maparg}, {"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}, {"matchadd", 2, 5, FEARG_1, ret_number, f_matchadd}, {"matchaddpos", 2, 5, FEARG_1, ret_number, f_matchaddpos}, diff --git a/src/map.c b/src/map.c --- a/src/map.c +++ b/src/map.c @@ -204,6 +204,86 @@ showmap( 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[!] {lhs} : show key mapping for {lhs} @@ -501,7 +581,8 @@ do_map( msg_start(); // 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 for (hash = 0; hash < 256 && !got_int; ++hash) @@ -519,7 +600,6 @@ do_map( // check entries with the same mode if ((mp->m_mode & mode) != 0 && mp->m_keylen == len - && unique && STRNCMP(mp->m_keys, keys, (size_t)len) == 0) { if (abbrev) @@ -759,58 +839,17 @@ do_map( continue; // have added the new entry already // Get here when adding a new entry to the maphash[] list or abbrlist. - mp = ALLOC_ONE(mapblock_T); - if (mp == NULL) + if (map_add(map_table, abbr_table, keys, rhs, orig_rhs, expr, + noremap, nowait, silent, mode, + abbrev, +#ifdef FEAT_EVAL + /* sid */ -1, /* lnum */ 0, +#endif + did_simplify && keyround == 1) == FAIL) { retval = 4; // no mem 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: @@ -2209,13 +2248,96 @@ get_maparg(typval_T *argvars, typval_T * dict_add_number(dict, "buffer", (long)buffer_local); dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L); dict_add_string(dict, "mode", mapmode); + dict_add_number(dict, "simplified", mp->m_simplified); vim_free(lhs); 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 + #if defined(MSWIN) || defined(MACOS_X) # define VIS_SEL (VISUAL+SELECTMODE) // abbreviation diff --git a/src/proto/map.pro b/src/proto/map.pro --- a/src/proto/map.pro +++ b/src/proto/map.pro @@ -18,6 +18,7 @@ int put_escstr(FILE *fd, char_u *strstar 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); 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 add_map(char_u *map, int mode); int langmap_adjust_mb(int c); diff --git a/src/testdir/test_maparg.vim b/src/testdir/test_maparg.vim --- a/src/testdir/test_maparg.vim +++ b/src/testdir/test_maparg.vim @@ -1,12 +1,12 @@ -" Tests for maparg(). +" Tests for maparg(), mapcheck() and mapset(). " Also test utf8 map with a 0x80 byte. " Also test mapcheck() -function s:SID() +func s:SID() return str2nr(matchstr(expand(''), '\zs\d\+\ze_SID$')) -endfun +endfunc -function Test_maparg() +funct Test_maparg() new set cpo-=< set encoding=utf8 @@ -18,23 +18,23 @@ function Test_maparg() call assert_equal("isfoo", maparg('foo')) call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo', \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, - \ 'rhs': 'isfoo', 'buffer': 0}, + \ 'simplified': 1, 'rhs': 'isfoo', 'buffer': 0}, \ maparg('foo', '', 0, 1)) call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar', 'mode': 'v', \ 'nowait': 0, 'expr': 1, 'sid': sid, 'lnum': lnum + 2, - \ 'rhs': 'isbar', 'buffer': 1}, + \ 'simplified': 0, 'rhs': 'isbar', 'buffer': 1}, \ 'bar'->maparg('', 0, 1)) let lnum = expand('') map foo bar call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo', 'mode': ' ', \ 'nowait': 1, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'bar', - \ 'buffer': 1}, + \ 'simplified': 0, 'buffer': 1}, \ maparg('foo', '', 0, 1)) let lnum = expand('') tmap baz foo call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'baz', 'mode': 't', \ 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'foo', - \ 'buffer': 0}, + \ 'simplified': 0, 'buffer': 0}, \ maparg('baz', 't', 0, 1)) map abc xx @@ -75,7 +75,7 @@ function Test_maparg() let d = maparg('esc', 'i', 1, 1) call assert_equal(['esc', "\\\", '!'], [d.lhs, d.rhs, d.mode]) abclear -endfunction +endfunc func Test_mapcheck() call assert_equal('', mapcheck('a')) @@ -116,7 +116,7 @@ func Test_mapcheck() unabbr ab endfunc -function Test_range_map() +func Test_range_map() new " Outside of the range, minimum inoremap a @@ -131,6 +131,31 @@ function Test_range_map() inoremap d execute "normal a\uf040\" call assert_equal("abcd", getline(1)) -endfunction +endfunc + +func One_mapset_test(keys) + exe 'nnoremap ' .. a:keys .. ' original' + let orig = maparg(a:keys, 'n', 0, 1) + call assert_equal(a:keys, orig.lhs) + call assert_equal('original', 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', 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('') +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 807, +/**/ 806, /**/ 805,