aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2022-08-01 22:19:30 +0800
committerGitHub <noreply@github.com>2022-08-01 22:19:30 +0800
commit8d1c55e4224fc990d18409905aeb51d54d9d78cb (patch)
treebb4379bde6144ab7a0e2436149730a276c3089d0
parentdb6e93c48df551e2906c9e0f4472f9e54cea3dd9 (diff)
parentd954e8da62faccd468896baf2fe06107196cf952 (diff)
downloadrneovim-8d1c55e4224fc990d18409905aeb51d54d9d78cb.tar.gz
rneovim-8d1c55e4224fc990d18409905aeb51d54d9d78cb.tar.bz2
rneovim-8d1c55e4224fc990d18409905aeb51d54d9d78cb.zip
Merge pull request #19602 from zeertzjq/vim-8.2.0807
vim-patch:8.2.{0807,0809,0812,0815,0832,1773,2804,4831,5106},9.0.0127: first part of mapset()
-rw-r--r--runtime/doc/builtin.txt27
-rw-r--r--src/nvim/digraph.c4
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/mapping.c264
-rw-r--r--src/nvim/mapping.h5
-rw-r--r--src/nvim/testdir/test_maparg.vim184
-rw-r--r--test/functional/api/keymap_spec.lua32
-rw-r--r--test/functional/legacy/075_maparg_spec.lua59
-rw-r--r--test/functional/vimscript/map_functions_spec.lua34
9 files changed, 441 insertions, 169 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index f844ae5aaf..e2a8e098d2 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -295,6 +295,8 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]])
rhs of mapping {name} in mode {mode}
mapcheck({name} [, {mode} [, {abbr}]])
String check for mappings matching {name}
+mapset({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}]]])
@@ -4716,6 +4718,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
@@ -4749,7 +4752,10 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
When {dict} is there and it is |TRUE| return a dictionary
containing all the information of the mapping with the
following items:
- "lhs" The {lhs} of the mapping.
+ "lhs" The {lhs} of the mapping as it would be typed
+ "lhsraw" The {lhs} of the mapping as raw bytes
+ "lhsrawalt" The {lhs} of the mapping as raw bytes, alternate
+ form, only present when it differs from "lhsraw"
"rhs" The {rhs} of the mapping as typed.
"silent" 1 for a |:map-silent| mapping, else 0.
"noremap" 1 if the {rhs} of the mapping is not remappable.
@@ -4768,6 +4774,9 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
"nowait" Do not wait for other, longer mappings.
(|:map-<nowait>|).
+ 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.
This function can be used to map a key even when it's already
@@ -4813,6 +4822,22 @@ mapcheck({name} [, {mode} [, {abbr}]]) *mapcheck()*
Can also be used as a |method|: >
GetKey()->mapcheck('n')
+mapset({mode}, {abbr}, {dict}) *mapset()*
+ Restore a mapping from a dictionary returned by |maparg()|.
+ {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,
+ 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)
+< 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
+ them, since they can differe.
+
+
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/nvim/digraph.c b/src/nvim/digraph.c
index 36f6d06512..733b3d3d5d 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -2121,7 +2121,7 @@ void ex_loadkeymap(exarg_T *eap)
vim_snprintf((char *)buf, sizeof(buf), "<buffer> %s %s",
((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].from,
((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].to);
- (void)do_map(0, buf, MODE_LANGMAP, false);
+ (void)do_map(MAPTYPE_MAP, buf, MODE_LANGMAP, false);
}
p_cpo = save_cpo;
@@ -2158,7 +2158,7 @@ static void keymap_unload(void)
for (int i = 0; i < curbuf->b_kmap_ga.ga_len; i++) {
vim_snprintf(buf, sizeof(buf), "<buffer> %s", kp[i].from);
- (void)do_map(1, (char_u *)buf, MODE_LANGMAP, false);
+ (void)do_map(MAPTYPE_UNMAP, (char_u *)buf, MODE_LANGMAP, false);
}
keymap_ga_clear(&curbuf->b_kmap_ga);
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 3db0d27018..c8eb0334fa 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -250,6 +250,7 @@ return {
map={args=2, base=1},
maparg={args={1, 4}, base=1},
mapcheck={args={1, 3}, base=1},
+ mapset={args=3, base=1},
match={args={2, 4}, base=1},
matchadd={args={2, 5}, base=1},
matchaddpos={args={2, 5}, base=1},
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index 93374a41bd..41ab236676 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -428,6 +428,66 @@ static int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *ma
return 0;
}
+/// @param args "rhs", "rhs_lua", "orig_rhs", "expr", "silent", "nowait", "replace_keycodes" and
+/// and "desc" fields are used.
+/// "rhs", "rhs_lua", "orig_rhs" fields are cleared if "simplified" is false.
+/// @param sid -1 to use current_sctx
+static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table, const char_u *keys,
+ MapArguments *args, int noremap, int mode, bool is_abbr, scid_T sid,
+ linenr_T lnum, bool simplified)
+{
+ mapblock_T *mp = xcalloc(1, sizeof(mapblock_T));
+
+ // If CTRL-C has been mapped, don't always use it for Interrupting.
+ if (*keys == Ctrl_C) {
+ if (map_table == buf->b_maphash) {
+ buf->b_mapped_ctrl_c |= mode;
+ } else {
+ mapped_ctrl_c |= mode;
+ }
+ }
+
+ mp->m_keys = vim_strsave(keys);
+ mp->m_str = args->rhs;
+ mp->m_orig_str = args->orig_rhs;
+ mp->m_luaref = args->rhs_lua;
+ if (!simplified) {
+ args->rhs = NULL;
+ args->orig_rhs = NULL;
+ args->rhs_lua = LUA_NOREF;
+ }
+ mp->m_keylen = (int)STRLEN(mp->m_keys);
+ mp->m_noremap = noremap;
+ mp->m_nowait = args->nowait;
+ mp->m_silent = args->silent;
+ mp->m_mode = mode;
+ mp->m_simplified = simplified;
+ mp->m_expr = args->expr;
+ mp->m_replace_keycodes = args->replace_keycodes;
+ 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;
+ nlua_set_sctx(&mp->m_script_ctx);
+ }
+ mp->m_desc = NULL;
+ if (args->desc != NULL) {
+ mp->m_desc = xstrdup(args->desc);
+ }
+
+ // add the new entry in front of the abbrlist or maphash[] list
+ if (is_abbr) {
+ mp->m_next = *abbr_table;
+ *abbr_table = mp;
+ } else {
+ const int n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
+ mp->m_next = map_table[n];
+ map_table[n] = mp;
+ }
+}
+
/// Sets or removes a mapping or abbreviation in buffer `buf`.
///
/// @param maptype @see do_map
@@ -452,7 +512,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
abbr_table = &first_abbr;
// For ":noremap" don't remap, otherwise do remap.
- if (maptype == 2) {
+ if (maptype == MAPTYPE_NOREMAP) {
noremap = REMAP_NONE;
} else {
noremap = REMAP_YES;
@@ -470,10 +530,10 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
const bool has_lhs = (args->lhs[0] != NUL);
const bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop;
- const bool do_print = !has_lhs || (maptype != 1 && !has_rhs);
+ const bool do_print = !has_lhs || (maptype != MAPTYPE_UNMAP && !has_rhs);
// check for :unmap without argument
- if (maptype == 1 && !has_lhs) {
+ if (maptype == MAPTYPE_UNMAP && !has_lhs) {
retval = 1;
goto theend;
}
@@ -507,13 +567,11 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
goto theend;
}
- if (is_abbrev && maptype != 1) {
- //
+ if (is_abbrev && maptype != MAPTYPE_UNMAP) {
// If an abbreviation ends in a keyword character, the
// rest must be all keyword-char or all non-keyword-char.
// Otherwise we won't be able to find the start of it in a
// vi-compatible way.
- //
int same = -1;
const int first = vim_iswordp(lhs);
@@ -551,7 +609,8 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
}
// Check if a new local mapping wasn't already defined globally.
- if (args->unique && map_table == buf->b_maphash && has_lhs && has_rhs && maptype != 1) {
+ if (args->unique && map_table == buf->b_maphash && has_lhs && has_rhs
+ && maptype != MAPTYPE_UNMAP) {
// need to loop over all global hash lists
for (int hash = 0; hash < 256 && !got_int; hash++) {
if (is_abbrev) {
@@ -581,7 +640,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
}
// When listing global mappings, also list buffer-local ones here.
- if (map_table != buf->b_maphash && !has_rhs && maptype != 1) {
+ if (map_table != buf->b_maphash && !has_rhs && maptype != MAPTYPE_UNMAP) {
// need to loop over all global hash lists
for (int hash = 0; hash < 256 && !got_int; hash++) {
if (is_abbrev) {
@@ -616,7 +675,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
// entry with a matching 'to' part. This was done to allow ":ab foo bar"
// to be unmapped by typing ":unab foo", where "foo" will be replaced by
// "bar" because of the abbreviation.
- for (int round = 0; (round == 0 || maptype == 1) && round <= 1
+ for (int round = 0; (round == 0 || maptype == MAPTYPE_UNMAP) && round <= 1
&& !did_it && !got_int; round++) {
int hash_start, hash_end;
if (has_lhs || is_abbrev) {
@@ -650,7 +709,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
p = mp->m_keys;
}
if (STRNCMP(p, lhs, (size_t)(n < len ? n : len)) == 0) {
- if (maptype == 1) {
+ if (maptype == MAPTYPE_UNMAP) {
// Delete entry.
// Only accept a full match. For abbreviations
// we ignore trailing space when matching with
@@ -746,7 +805,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
}
}
- if (maptype == 1) {
+ if (maptype == MAPTYPE_UNMAP) {
// delete entry
if (!did_it) {
if (!keyround1_simplified) {
@@ -780,51 +839,10 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
}
// Get here when adding a new entry to the maphash[] list or abbrlist.
- mp = xmalloc(sizeof(mapblock_T));
-
- // If CTRL-C has been mapped, don't always use it for Interrupting.
- if (*lhs == Ctrl_C) {
- if (map_table == buf->b_maphash) {
- buf->b_mapped_ctrl_c |= mode;
- } else {
- mapped_ctrl_c |= mode;
- }
- }
-
- mp->m_keys = vim_strsave(lhs);
- mp->m_str = args->rhs;
- mp->m_orig_str = args->orig_rhs;
- mp->m_luaref = args->rhs_lua;
- if (!keyround1_simplified) {
- args->rhs = NULL;
- args->orig_rhs = NULL;
- args->rhs_lua = LUA_NOREF;
- }
- mp->m_keylen = (int)STRLEN(mp->m_keys);
- mp->m_noremap = noremap;
- mp->m_nowait = args->nowait;
- mp->m_silent = args->silent;
- mp->m_mode = mode;
- mp->m_simplified = keyround1_simplified; // Notice this when porting patch 8.2.0807
- mp->m_expr = args->expr;
- mp->m_replace_keycodes = args->replace_keycodes;
- mp->m_script_ctx = current_sctx;
- mp->m_script_ctx.sc_lnum += sourcing_lnum;
- nlua_set_sctx(&mp->m_script_ctx);
- mp->m_desc = NULL;
- if (args->desc != NULL) {
- mp->m_desc = xstrdup(args->desc);
- }
-
- // add the new entry in front of the abbrlist or maphash[] list
- if (is_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;
- }
+ map_add(buf, map_table, abbr_table, lhs, args, noremap, mode, is_abbrev,
+ -1, // sid
+ 0, // lnum
+ keyround1_simplified);
}
theend:
@@ -863,7 +881,9 @@ theend:
/// for :cabbr mode is MODE_CMDLINE
/// ```
///
-/// @param maptype 0 for |:map|, 1 for |:unmap|, 2 for |noremap|.
+/// @param maptype MAPTYPE_MAP for |:map|
+/// MAPTYPE_UNMAP for |:unmap|
+/// MAPTYPE_NOREMAP for |noremap|.
/// @param arg C-string containing the arguments of the map/abbrev
/// command, i.e. everything except the initial `:[X][nore]map`.
/// - Cannot be a read-only string; it will be modified.
@@ -880,7 +900,7 @@ theend:
int do_map(int maptype, char_u *arg, int mode, bool is_abbrev)
{
MapArguments parsed_args;
- int result = str_to_mapargs(arg, maptype == 1, &parsed_args);
+ int result = str_to_mapargs(arg, maptype == MAPTYPE_UNMAP, &parsed_args);
switch (result) {
case 0:
break;
@@ -1977,9 +1997,9 @@ void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// @param mp The maphash that contains the mapping information
/// @param buffer_value The "buffer" value
/// @param compatible True for compatible with old maparg() dict
-static void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, long buffer_value,
- bool compatible)
- FUNC_ATTR_NONNULL_ALL
+static void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp,
+ const char *lhsrawalt, long buffer_value, bool compatible)
+ FUNC_ATTR_NONNULL_ARG(1, 2)
{
char *const lhs = str2special_save((const char *)mp->m_keys,
compatible, !compatible);
@@ -2011,6 +2031,11 @@ static void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, l
tv_dict_add_allocated_str(dict, S_LEN("desc"), xstrdup(mp->m_desc));
}
tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs);
+ tv_dict_add_str(dict, S_LEN("lhsraw"), (const char *)mp->m_keys);
+ if (lhsrawalt != NULL) {
+ // Also add the value for the simplified entry.
+ tv_dict_add_str(dict, S_LEN("lhsrawalt"), lhsrawalt);
+ }
tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value);
tv_dict_add_nr(dict, S_LEN("script"), mp->m_noremap == REMAP_SCRIPT ? 1 : 0);
tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0);
@@ -2027,18 +2052,6 @@ static void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, l
static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
{
- char *keys_buf = NULL;
- char_u *alt_keys_buf = NULL;
- bool did_simplify = false;
- char_u *rhs;
- LuaRef rhs_lua;
- int mode;
- bool abbr = false;
- bool get_dict = false;
- mapblock_T *mp = NULL;
- int buffer_local;
- int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
-
// Return empty string for failure.
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -2048,8 +2061,11 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
return;
}
- char buf[NUMBUFLEN];
const char *which;
+ char buf[NUMBUFLEN];
+ bool abbr = false;
+ bool get_dict = false;
+
if (argvars[1].v_type != VAR_UNKNOWN) {
which = tv_get_string_buf_chk(&argvars[1], buf);
if (argvars[2].v_type != VAR_UNKNOWN) {
@@ -2065,13 +2081,19 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
return;
}
- mode = get_map_mode((char **)&which, 0);
+ char *keys_buf = NULL;
+ char_u *alt_keys_buf = NULL;
+ bool did_simplify = false;
+ const int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
+ const int mode = get_map_mode((char **)&which, 0);
char_u *keys_simplified
- = (char_u *)replace_termcodes(keys,
- STRLEN(keys), &keys_buf, flags, &did_simplify,
+ = (char_u *)replace_termcodes(keys, STRLEN(keys), &keys_buf, flags, &did_simplify,
CPO_TO_CPO_FLAGS);
- rhs = check_map(keys_simplified, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
+ mapblock_T *mp = NULL;
+ int buffer_local;
+ LuaRef rhs_lua;
+ char_u *rhs = check_map(keys_simplified, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
if (did_simplify) {
// When the lhs is being simplified the not-simplified keys are
// preferred for printing, like in do_map().
@@ -2100,7 +2122,8 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
tv_dict_alloc_ret(rettv);
if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) {
// Return a dictionary.
- mapblock_fill_dict(rettv->vval.v_dict, mp, buffer_local, true);
+ mapblock_fill_dict(rettv->vval.v_dict, mp, did_simplify ? (char *)keys_simplified : NULL,
+ buffer_local, true);
}
}
@@ -2108,6 +2131,74 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
xfree(alt_keys_buf);
}
+/// "mapset()" function
+void f_mapset(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ char buf[NUMBUFLEN];
+ const char *which = tv_get_string_buf_chk(&argvars[0], buf);
+ if (which == NULL) {
+ return;
+ }
+ const int mode = get_map_mode((char **)&which, 0);
+ const bool is_abbr = tv_get_number(&argvars[1]) != 0;
+
+ if (argvars[2].v_type != VAR_DICT) {
+ emsg(_(e_dictreq));
+ return;
+ }
+ dict_T *d = argvars[2].vval.v_dict;
+
+ // Get the values in the same order as above in get_maparg().
+ char *lhs = tv_dict_get_string(d, "lhs", false);
+ char *lhsraw = tv_dict_get_string(d, "lhsraw", false);
+ char *lhsrawalt = tv_dict_get_string(d, "lhsrawalt", false);
+ char *rhs = tv_dict_get_string(d, "rhs", false);
+ if (lhs == NULL || lhsraw == NULL || rhs == NULL) {
+ emsg(_("E460: entries missing in mapset() dict argument"));
+ return;
+ }
+ char *orig_rhs = rhs;
+ char *arg_buf = NULL;
+ rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, REPTERM_DO_LT, NULL, CPO_TO_CPO_FLAGS);
+
+ int noremap = tv_dict_get_number(d, "noremap") ? REMAP_NONE : 0;
+ if (tv_dict_get_number(d, "script") != 0) {
+ noremap = REMAP_SCRIPT;
+ }
+ MapArguments args = { // TODO(zeertzjq): support restoring "callback"?
+ .rhs = (char_u *)rhs,
+ .rhs_lua = LUA_NOREF,
+ .orig_rhs = vim_strsave((char_u *)orig_rhs),
+ .expr = tv_dict_get_number(d, "expr") != 0,
+ .silent = tv_dict_get_number(d, "silent") != 0,
+ .nowait = tv_dict_get_number(d, "nowait") != 0,
+ .replace_keycodes = tv_dict_get_number(d, "replace_keycodes") != 0,
+ .desc = tv_dict_get_string(d, "desc", false),
+ };
+ scid_T sid = (scid_T)tv_dict_get_number(d, "sid");
+ linenr_T lnum = (linenr_T)tv_dict_get_number(d, "lnum");
+ bool buffer = tv_dict_get_number(d, "buffer") != 0;
+ // mode from the dict is not used
+
+ mapblock_T **map_table = buffer ? curbuf->b_maphash : maphash;
+ mapblock_T **abbr_table = buffer ? &curbuf->b_first_abbr : &first_abbr;
+
+ // Delete any existing mapping for this lhs and mode.
+ MapArguments unmap_args = MAP_ARGUMENTS_INIT;
+ set_maparg_lhs_rhs(lhs, strlen(lhs), rhs, strlen(rhs), LUA_NOREF, 0, &unmap_args);
+ unmap_args.buffer = buffer;
+ buf_do_map(MAPTYPE_UNMAP, &unmap_args, mode, false, curbuf);
+ xfree(unmap_args.rhs);
+ xfree(unmap_args.orig_rhs);
+
+ if (lhsrawalt != NULL) {
+ map_add(curbuf, map_table, abbr_table, (char_u *)lhsrawalt, &args, noremap, mode, is_abbr,
+ sid, lnum, true);
+ }
+ map_add(curbuf, map_table, abbr_table, (char_u *)lhsraw, &args, noremap, mode, is_abbr,
+ sid, lnum, false);
+}
+
/// "maparg()" function
void f_maparg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
@@ -2130,11 +2221,11 @@ void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// @param buffer If true, make a buffer-local mapping for curbuf
void add_map(char *lhs, char *rhs, int mode, bool buffer)
{
- MapArguments args = { 0 };
+ MapArguments args = MAP_ARGUMENTS_INIT;
set_maparg_lhs_rhs(lhs, strlen(lhs), rhs, strlen(rhs), LUA_NOREF, 0, &args);
args.buffer = buffer;
- buf_do_map(2, &args, mode, false, curbuf);
+ buf_do_map(MAPTYPE_NOREMAP, &args, mode, false, curbuf);
xfree(args.rhs);
xfree(args.orig_rhs);
}
@@ -2314,7 +2405,8 @@ static void do_exmap(exarg_T *eap, int isabbrev)
char *cmdp = eap->cmd;
mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
- switch (do_map((*cmdp == 'n') ? 2 : (*cmdp == 'u'),
+ switch (do_map((*cmdp == 'n') ? MAPTYPE_NOREMAP
+ : (*cmdp == 'u') ? MAPTYPE_UNMAP : MAPTYPE_MAP,
(char_u *)eap->arg, mode, isabbrev)) {
case 1:
emsg(_(e_invarg));
@@ -2474,11 +2566,11 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod
}
// buf_do_map() reads noremap/unmap as its own argument.
- int maptype_val = 0;
+ int maptype_val = MAPTYPE_MAP;
if (is_unmap) {
- maptype_val = 1;
+ maptype_val = MAPTYPE_UNMAP;
} else if (is_noremap) {
- maptype_val = 2;
+ maptype_val = MAPTYPE_NOREMAP;
}
switch (buf_do_map(maptype_val, &parsed_args, mode_val, 0, target_buf)) {
@@ -2536,7 +2628,7 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua)
}
// Check for correct mode
if (int_mode & current_maphash->m_mode) {
- mapblock_fill_dict(dict, current_maphash, buffer_value, false);
+ mapblock_fill_dict(dict, current_maphash, NULL, buffer_value, false);
Object api_dict = vim_to_object((typval_T[]) { { .v_type = VAR_DICT,
.vval.v_dict = dict } });
if (from_lua) {
diff --git a/src/nvim/mapping.h b/src/nvim/mapping.h
index 98d0074075..7c48c3bce2 100644
--- a/src/nvim/mapping.h
+++ b/src/nvim/mapping.h
@@ -48,6 +48,11 @@ typedef struct map_arguments MapArguments;
#define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, false, \
{ 0 }, 0, { 0 }, 0, NULL, 0, LUA_NOREF, false, NULL, 0, NULL }
+// Used for the first argument of do_map()
+#define MAPTYPE_MAP 0
+#define MAPTYPE_UNMAP 1
+#define MAPTYPE_NOREMAP 2
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mapping.h.generated.h"
#endif
diff --git a/src/nvim/testdir/test_maparg.vim b/src/nvim/testdir/test_maparg.vim
index f9429a8020..dad4c81a7b 100644
--- a/src/nvim/testdir/test_maparg.vim
+++ b/src/nvim/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('<sfile>'), '<SNR>\zs\d\+\ze_SID$'))
-endfun
+endfunc
-function Test_maparg()
+func Test_maparg()
new
set cpo-=<
set encoding=utf8
@@ -17,24 +17,28 @@ function Test_maparg()
vnoremap <script> <buffer> <expr> <silent> bar isbar
call assert_equal("is<F4>foo", maparg('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,
- \ 'rhs': 'is<F4>foo', 'buffer': 0},
- \ maparg('foo<C-V>', '', 0, 1))
- call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar', 'mode': 'v',
+ \ 'lhsraw': "foo\x80\xfc\x04V", 'lhsrawalt': "foo\x16",
+ \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1,
+ \ 'rhs': 'is<F4>foo', 'buffer': 0},
+ \ maparg('foo<C-V>', '', 0, 1))
+ call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar',
+ \ 'lhsraw': 'bar', 'mode': 'v',
\ 'nowait': 0, 'expr': 1, 'sid': sid, 'lnum': lnum + 2,
- \ 'rhs': 'isbar', 'buffer': 1},
+ \ 'rhs': 'isbar', 'buffer': 1},
\ 'bar'->maparg('', 0, 1))
let lnum = expand('<sflnum>')
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',
+ \ 'lhsraw': 'foo', 'mode': ' ',
\ 'nowait': 1, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'bar',
- \ 'buffer': 1},
+ \ 'buffer': 1},
\ maparg('foo', '', 0, 1))
let lnum = expand('<sflnum>')
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',
+ \ 'lhsraw': 'baz', 'mode': 't',
\ 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'foo',
- \ 'buffer': 0},
+ \ 'buffer': 0},
\ maparg('baz', 't', 0, 1))
map abc x<char-114>x
@@ -81,6 +85,12 @@ function Test_maparg()
call assert_equal(['{', 'w', 's'], [d.lhs, d.rhs, d.mode])
sunmap {
+ map <C-I> foo
+ unmap <Tab>
+ " This used to cause a segfault
+ call maparg('<C-I>', '', 0, 1)
+ unmap <C-I>
+
map abc <Nop>
call assert_equal("<Nop>", maparg('abc'))
unmap abc
@@ -89,7 +99,8 @@ function Test_maparg()
let d = maparg('esc', 'i', 1, 1)
call assert_equal(['esc', "\<C-V>\<C-V>\<Esc>", '!'], [d.lhs, d.rhs, d.mode])
abclear
-endfunction
+ unlet d
+endfunc
func Test_mapcheck()
call assert_equal('', mapcheck('a'))
@@ -130,7 +141,7 @@ func Test_mapcheck()
unabbr ab
endfunc
-function Test_range_map()
+func Test_range_map()
new
" Outside of the range, minimum
inoremap <Char-0x1040> a
@@ -145,6 +156,147 @@ function Test_range_map()
inoremap <Char-0xf040> d
execute "normal a\uf040\<Esc>"
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>')
+
+ " Check <> key conversion
+ new
+ inoremap K one<Left>x
+ call feedkeys("iK\<Esc>", 'xt')
+ call assert_equal('onxe', getline(1))
+
+ let orig = maparg('K', 'i', 0, 1)
+ call assert_equal('K', orig.lhs)
+ call assert_equal('one<Left>x', orig.rhs)
+ call assert_equal('i', orig.mode)
+
+ iunmap K
+ let d = maparg('K', 'i', 0, 1)
+ call assert_equal({}, d)
+
+ call mapset('i', 0, orig)
+ call feedkeys("SK\<Esc>", 'xt')
+ call assert_equal('onxe', getline(1))
+
+ iunmap K
+
+ " Test literal <CR> using a backslash
+ let cpo_save = &cpo
+ set cpo-=B
+ inoremap K one\<CR>two
+ call feedkeys("SK\<Esc>", 'xt')
+ call assert_equal('one<CR>two', getline(1))
+
+ let orig = maparg('K', 'i', 0, 1)
+ call assert_equal('K', orig.lhs)
+ call assert_equal('one\<CR>two', orig.rhs)
+ call assert_equal('i', orig.mode)
+
+ iunmap K
+ let d = maparg('K', 'i', 0, 1)
+ call assert_equal({}, d)
+
+ call mapset('i', 0, orig)
+ call feedkeys("SK\<Esc>", 'xt')
+ call assert_equal('one<CR>two', getline(1))
+
+ iunmap K
+
+ " Test literal <CR> using CTRL-V
+ inoremap K one<CR>two
+ call feedkeys("SK\<Esc>", 'xt')
+ call assert_equal('one<CR>two', getline(1))
+
+ let orig = maparg('K', 'i', 0, 1)
+ call assert_equal('K', orig.lhs)
+ call assert_equal("one\x16<CR>two", orig.rhs)
+ call assert_equal('i', orig.mode)
+
+ iunmap K
+ let d = maparg('K', 'i', 0, 1)
+ call assert_equal({}, d)
+
+ call mapset('i', 0, orig)
+ call feedkeys("SK\<Esc>", 'xt')
+ call assert_equal('one<CR>two', getline(1))
+
+ iunmap K
+ let &cpo = cpo_save
+ bwipe!
+
+ call assert_fails('call mapset([], v:false, {})', 'E730:')
+endfunc
+
+func Check_ctrlb_map(d, check_alt)
+ call assert_equal('<C-B>', a:d.lhs)
+ if a:check_alt
+ call assert_equal("\x80\xfc\x04B", a:d.lhsraw)
+ call assert_equal("\x02", a:d.lhsrawalt)
+ else
+ call assert_equal("\x02", a:d.lhsraw)
+ endif
+endfunc
+
+func Test_map_local()
+ nmap a global
+ nmap <buffer>a local
+
+ let prev_map_list = split(execute('nmap a'), "\n")
+ call assert_match('n\s*a\s*@local', prev_map_list[0])
+ call assert_match('n\s*a\s*global', prev_map_list[1])
+
+ let mapping = maparg('a', 'n', 0, 1)
+ call assert_equal(1, mapping.buffer)
+ let mapping.rhs = 'new_local'
+ call mapset('n', 0, mapping)
+
+ " Check that the global mapping is left untouched.
+ let map_list = split(execute('nmap a'), "\n")
+ call assert_match('n\s*a\s*@new_local', map_list[0])
+ call assert_match('n\s*a\s*global', map_list[1])
+
+ nunmap a
+endfunc
+
+func Test_map_restore()
+ " Test restoring map with alternate keycode
+ nmap <C-B> back
+ let d = maparg('<C-B>', 'n', 0, 1)
+ call Check_ctrlb_map(d, 1)
+ let dsimp = maparg("\x02", 'n', 0, 1)
+ call Check_ctrlb_map(dsimp, 0)
+ nunmap <C-B>
+ call mapset('n', 0, d)
+ let d = maparg('<C-B>', 'n', 0, 1)
+ call Check_ctrlb_map(d, 1)
+ let dsimp = maparg("\x02", 'n', 0, 1)
+ call Check_ctrlb_map(dsimp, 0)
+
+ nunmap <C-B>
+
+endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua
index 23c0a14d39..712889828e 100644
--- a/test/functional/api/keymap_spec.lua
+++ b/test/functional/api/keymap_spec.lua
@@ -25,6 +25,7 @@ describe('nvim_get_keymap', function()
local foo_bar_string = 'nnoremap foo bar'
local foo_bar_map_table = {
lhs='foo',
+ lhsraw='foo',
script=0,
silent=0,
rhs='bar',
@@ -56,6 +57,7 @@ describe('nvim_get_keymap', function()
command('nnoremap foo_longer bar_longer')
local foolong_bar_map_table = shallowcopy(foo_bar_map_table)
foolong_bar_map_table['lhs'] = 'foo_longer'
+ foolong_bar_map_table['lhsraw'] = 'foo_longer'
foolong_bar_map_table['rhs'] = 'bar_longer'
eq({foolong_bar_map_table, foo_bar_map_table},
@@ -87,6 +89,7 @@ describe('nvim_get_keymap', function()
command('nnoremap foo_longer bar_longer')
local foolong_bar_map_table = shallowcopy(foo_bar_map_table)
foolong_bar_map_table['lhs'] = 'foo_longer'
+ foolong_bar_map_table['lhsraw'] = 'foo_longer'
foolong_bar_map_table['rhs'] = 'bar_longer'
local buffer_table = shallowcopy(foo_bar_map_table)
@@ -283,6 +286,16 @@ describe('nvim_get_keymap', function()
command('onoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\')
command('onoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\')
+ -- wrapper around get_keymap() that drops "lhsraw" and "lhsrawalt" which are hard to check
+ local function get_keymap_noraw(...)
+ local ret = meths.get_keymap(...)
+ for _, item in ipairs(ret) do
+ item.lhsraw = nil
+ item.lhsrawalt = nil
+ end
+ return ret
+ end
+
for _, cmd in ipairs({
'set cpo-=B',
'set cpo+=B',
@@ -290,22 +303,23 @@ describe('nvim_get_keymap', function()
command(cmd)
eq({cpomap('\\<C-C><C-C><lt>C-c>\\', '\\<C-D><C-D><lt>C-d>\\', 'n'),
cpomap('\\<C-A><C-A><lt>C-a>\\', '\\<C-B><C-B><lt>C-b>\\', 'n')},
- meths.get_keymap('n'))
+ get_keymap_noraw('n'))
eq({cpomap('\\<C-C><C-C><lt>C-c>\\', '\\<C-D><C-D><lt>C-d>\\', 'x'),
cpomap('\\<C-A><C-A><lt>C-a>\\', '\\<C-B><C-B><lt>C-b>\\', 'x')},
- meths.get_keymap('x'))
+ get_keymap_noraw('x'))
eq({cpomap('<lt>C-c><C-C><lt>C-c> ', '<lt>C-d><C-D><lt>C-d>', 's'),
cpomap('<lt>C-a><C-A><lt>C-a> ', '<lt>C-b><C-B><lt>C-b>', 's')},
- meths.get_keymap('s'))
+ get_keymap_noraw('s'))
eq({cpomap('<lt>C-c><C-C><lt>C-c> ', '<lt>C-d><C-D><lt>C-d>', 'o'),
cpomap('<lt>C-a><C-A><lt>C-a> ', '<lt>C-b><C-B><lt>C-b>', 'o')},
- meths.get_keymap('o'))
+ get_keymap_noraw('o'))
end
end)
it('always uses space for space and bar for bar', function()
local space_table = {
lhs='| |',
+ lhsraw='| |',
rhs='| |',
mode='n',
script=0,
@@ -340,6 +354,7 @@ describe('nvim_get_keymap', function()
mapargs[1].callback = nil
eq({
lhs='asdf',
+ lhsraw='asdf',
script=0,
silent=0,
expr=0,
@@ -356,6 +371,7 @@ describe('nvim_get_keymap', function()
meths.set_keymap('n', 'lhs', 'rhs', {desc="map description"})
eq({
lhs='lhs',
+ lhsraw='lhs',
rhs='rhs',
script=0,
silent=0,
@@ -413,7 +429,11 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
-- Gets a maparg() dict from Nvim, if one exists.
local function get_mapargs(mode, lhs)
- return funcs.maparg(lhs, normalize_mapmode(mode), false, true)
+ local mapargs = funcs.maparg(lhs, normalize_mapmode(mode), false, true)
+ -- drop "lhsraw" and "lhsrawalt" which are hard to check
+ mapargs.lhsraw = nil
+ mapargs.lhsrawalt = nil
+ return mapargs
end
it('error on empty LHS', function()
@@ -817,6 +837,8 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
local mapargs = funcs.maparg('asdf', 'n', false, true)
assert(type(mapargs.callback) == 'number', 'callback is not luaref number')
mapargs.callback = nil
+ mapargs.lhsraw = nil
+ mapargs.lhsrawalt = nil
eq(generate_mapargs('n', 'asdf', nil, {sid=sid_lua}), mapargs)
end)
diff --git a/test/functional/legacy/075_maparg_spec.lua b/test/functional/legacy/075_maparg_spec.lua
deleted file mode 100644
index ad6c190104..0000000000
--- a/test/functional/legacy/075_maparg_spec.lua
+++ /dev/null
@@ -1,59 +0,0 @@
--- Tests for maparg().
--- Also test utf8 map with a 0x80 byte.
-
-local helpers = require('test.functional.helpers')(after_each)
-local clear, feed = helpers.clear, helpers.feed
-local command, expect = helpers.command, helpers.expect
-local poke_eventloop = helpers.poke_eventloop
-
-describe('maparg()', function()
- setup(clear)
-
- it('is working', function()
- command('set cpo-=<')
-
- -- Test maparg() with a string result
- command('map foo<C-V> is<F4>foo')
- command('vnoremap <script> <buffer> <expr> <silent> bar isbar')
- command([[call append('$', maparg('foo<C-V>'))]])
- command([[call append('$', string(maparg('foo<C-V>', '', 0, 1)))]])
- command([[call append('$', string(maparg('bar', '', 0, 1)))]])
- command('map <buffer> <nowait> foo bar')
- command([[call append('$', string(maparg('foo', '', 0, 1)))]])
- command('map abc x<char-114>x')
- command([[call append('$', maparg('abc'))]])
- command('map abc y<S-char-114>y')
- command([[call append('$', maparg('abc'))]])
- feed('Go<esc>:<cr>')
- poke_eventloop()
-
- -- Outside of the range, minimum
- command('inoremap <Char-0x1040> a')
- command([[execute "normal a\u1040\<Esc>"]])
-
- -- Inside of the range, minimum
- command('inoremap <Char-0x103f> b')
- command([[execute "normal a\u103f\<Esc>"]])
-
- -- Inside of the range, maximum
- command('inoremap <Char-0xf03f> c')
- command([[execute "normal a\uf03f\<Esc>"]])
-
- -- Outside of the range, maximum
- command('inoremap <Char-0xf040> d')
- command([[execute "normal a\uf040\<Esc>"]])
-
- -- Remove empty line
- command('1d')
-
- -- Assert buffer contents.
- expect([[
- is<F4>foo
- {'lnum': 0, 'script': 0, 'silent': 0, 'noremap': 0, 'lhs': 'foo<C-V>', 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': 0, 'rhs': 'is<F4>foo', 'buffer': 0}
- {'lnum': 0, 'script': 1, 'silent': 1, 'noremap': 1, 'lhs': 'bar', 'mode': 'v', 'nowait': 0, 'expr': 1, 'sid': 0, 'rhs': 'isbar', 'buffer': 1}
- {'lnum': 0, 'script': 0, 'silent': 0, 'noremap': 0, 'lhs': 'foo', 'mode': ' ', 'nowait': 1, 'expr': 0, 'sid': 0, 'rhs': 'bar', 'buffer': 1}
- xrx
- yRy
- abcd]])
- end)
-end)
diff --git a/test/functional/vimscript/map_functions_spec.lua b/test/functional/vimscript/map_functions_spec.lua
index 275c72d212..aa64006de0 100644
--- a/test/functional/vimscript/map_functions_spec.lua
+++ b/test/functional/vimscript/map_functions_spec.lua
@@ -3,7 +3,10 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local eq = helpers.eq
local eval = helpers.eval
+local expect = helpers.expect
+local feed = helpers.feed
local funcs = helpers.funcs
+local meths = helpers.meths
local nvim = helpers.nvim
local source = helpers.source
local command = helpers.command
@@ -13,6 +16,7 @@ describe('maparg()', function()
local foo_bar_map_table = {
lhs='foo',
+ lhsraw='foo',
script=0,
silent=0,
rhs='bar',
@@ -141,6 +145,7 @@ describe('maparg()', function()
local function acmap(lhs, rhs)
return {
lhs = ac(lhs),
+ lhsraw = ac(lhs),
rhs = ac(rhs),
buffer = 0,
@@ -161,3 +166,32 @@ describe('maparg()', function()
eq(acmap('e`', 'f`'), funcs.maparg(ac('e`'), 'n', 0, 1))
end)
end)
+
+describe('mapset()', function()
+ before_each(clear)
+
+ it('can restore mapping description from the dict returned by maparg()', function()
+ meths.set_keymap('n', 'lhs', 'rhs', {desc = 'map description'})
+ eq('\nn lhs rhs\n map description',
+ helpers.exec_capture("nmap lhs"))
+ local mapargs = funcs.maparg('lhs', 'n', false, true)
+ meths.del_keymap('n', 'lhs')
+ eq('\nNo mapping found', helpers.exec_capture("nmap lhs"))
+ funcs.mapset('n', false, mapargs)
+ eq('\nn lhs rhs\n map description',
+ helpers.exec_capture("nmap lhs"))
+ end)
+
+ it('can restore "replace_keycodes" from the dict returned by maparg()', function()
+ meths.set_keymap('i', 'foo', [['<l' .. 't>']], {expr = true, replace_keycodes = true})
+ feed('Afoo')
+ expect('<')
+ local mapargs = funcs.maparg('foo', 'i', false, true)
+ meths.set_keymap('i', 'foo', [['<l' .. 't>']], {expr = true})
+ feed('foo')
+ expect('<<lt>')
+ funcs.mapset('i', false, mapargs)
+ feed('foo')
+ expect('<<lt><')
+ end)
+end)