aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/builtin.txt74
-rw-r--r--runtime/doc/map.txt2
-rw-r--r--runtime/doc/usr_41.txt1
-rw-r--r--runtime/lua/vim/_meta/vimfn.lua80
-rw-r--r--runtime/pack/dist/opt/termdebug/plugin/termdebug.vim9
-rw-r--r--src/nvim/eval.lua82
-rw-r--r--src/nvim/mapping.c160
-rw-r--r--test/functional/api/keymap_spec.lua72
-rw-r--r--test/functional/vimscript/map_functions_spec.lua8
-rw-r--r--test/old/testdir/test_map_functions.vim (renamed from test/old/testdir/test_maparg.vim)265
10 files changed, 679 insertions, 74 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 35c94d6c74..bc4d1f30c9 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -4133,7 +4133,8 @@ 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
characters translated like in the output of the ":map" command
- listing.
+ listing. When {dict} is TRUE a dictionary is returned, see
+ below. To get a list of all mappings see |maplist()|.
When there is no mapping for {name}, an empty String is
returned if {dict} is FALSE, otherwise returns an empty Dict.
@@ -4161,7 +4162,7 @@ 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:
+ following items: *mapping-dict*
"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
@@ -4180,9 +4181,16 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
(|mapmode-ic|)
"sid" The script local ID, used for <sid> mappings
(|<SID>|). Negative for special contexts.
+ "scriptversion" The version of the script, always 1.
"lnum" The line number in "sid", zero if unknown.
"nowait" Do not wait for other, longer mappings.
(|:map-<nowait>|).
+ "abbr" True if this is an |abbreviation|.
+ "mode_bits" Nvim's internal binary representation of "mode".
+ |mapset()| ignores this; only "mode" is used.
+ See |maplist()| for usage examples. The values
+ are from src/nvim/vim.h and may change in the
+ future.
The dictionary can be used to restore a mapping with
|mapset()|.
@@ -4226,6 +4234,37 @@ mapcheck({name} [, {mode} [, {abbr}]]) *mapcheck()*
< This avoids adding the "_vv" mapping when there already is a
mapping for "_v" or for "_vvv".
+maplist([{abbr}]) *maplist()*
+ Returns a |List| of all mappings. Each List item is a |Dict|,
+ the same as what is returned by |maparg()|, see
+ |mapping-dict|. When {abbr} is there and it is |TRUE| use
+ abbreviations instead of mappings.
+
+ Example to show all mappings with "MultiMatch" in rhs: >vim
+ echo maplist()->filter({_, m ->
+ \ match(get(m, 'rhs', ''), 'MultiMatch') >= 0
+ \ })
+< It can be tricky to find mappings for particular |:map-modes|.
+ |mapping-dict|'s "mode_bits" can simplify this. For example,
+ the mode_bits for Normal, Insert or Command-line modes are
+ 0x19. To find all the mappings available in those modes you
+ can do: >vim
+ let saved_maps = []
+ for m in maplist()
+ if and(m.mode_bits, 0x19) != 0
+ eval saved_maps->add(m)
+ endif
+ endfor
+ echo saved_maps->mapnew({_, m -> m.lhs})
+< The values of the mode_bits are defined in Nvim's
+ src/nvim/vim.h file and they can be discovered at runtime
+ using |:map-commands| and "maplist()". Example: >vim
+ omap xyzzy <Nop>
+ let op_bit = maplist()->filter(
+ \ {_, m -> m.lhs == 'xyzzy'})[0].mode_bits
+ ounmap xyzzy
+ echo printf("Operator-pending mode bit: 0x%x", op_bit)
+
mapnew({expr1}, {expr2}) *mapnew()*
Like |map()| but instead of replacing items in {expr1} a new
List or Dictionary is created and returned. {expr1} remains
@@ -4233,9 +4272,17 @@ mapnew({expr1}, {expr2}) *mapnew()*
don't want that use |deepcopy()| first.
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*
+ Restore a mapping from a dictionary, possibly returned by
+ |maparg()| or |maplist()|. A buffer mapping, when dict.buffer
+ is true, is set on the current buffer; it is up to the caller
+ to ensure 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,
not the "mode" entry in {dict}.
Example for saving and restoring a mapping: >vim
@@ -4244,8 +4291,21 @@ mapset({mode}, {abbr}, {dict}) *mapset()*
" ...
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 differ.
+ e.g. with `:map!`, you need to save/restore the mapping for
+ 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: >vim
+ let save_maps = maplist()->filter(
+ \ {_, m -> m.lhs == 'K'})
+ nnoremap K somethingelse
+ cnoremap K somethingelse2
+ " ...
+ unmap K
+ for d in save_maps
+ call mapset(d)
+ endfor
match({expr}, {pat} [, {start} [, {count}]]) *match()*
When {expr} is a |List| then this returns the index of the
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt
index 56e58baad8..6f61259af0 100644
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -955,7 +955,7 @@ operator to add quotes around text in the current line: >
\ ->setline(".")}'<CR>g@
==============================================================================
-2. Abbreviations *abbreviations* *Abbreviations*
+2. Abbreviations *abbreviation* *abbreviations* *Abbreviations*
Abbreviations are used in Insert mode, Replace mode and Command-line mode.
If you enter a word that is an abbreviation, it is replaced with the word it
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index c8b31b2d5b..e206a804f4 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -1015,6 +1015,7 @@ Mappings and Menus: *mapping-functions*
hasmapto() check if a mapping exists
mapcheck() check if a matching mapping exists
maparg() get rhs of a mapping
+ maplist() get list of all mappings
mapset() restore a mapping
menu_info() get information about a menu item
wildmenumode() check if the wildmode is active
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index 0563f9dd5f..d1e676ef70 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -4994,7 +4994,8 @@ function vim.fn.map(expr1, expr2) end
--- When {dict} is omitted or zero: Return the rhs of mapping
--- {name} in mode {mode}. The returned String has special
--- characters translated like in the output of the ":map" command
---- listing.
+--- listing. When {dict} is TRUE a dictionary is returned, see
+--- below. To get a list of all mappings see |maplist()|.
---
--- When there is no mapping for {name}, an empty String is
--- returned if {dict} is FALSE, otherwise returns an empty Dict.
@@ -5022,7 +5023,7 @@ function vim.fn.map(expr1, expr2) end
---
--- When {dict} is there and it is |TRUE| return a dictionary
--- containing all the information of the mapping with the
---- following items:
+--- following items: *mapping-dict*
--- "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
@@ -5041,9 +5042,16 @@ function vim.fn.map(expr1, expr2) end
--- (|mapmode-ic|)
--- "sid" The script local ID, used for <sid> mappings
--- (|<SID>|). Negative for special contexts.
+--- "scriptversion" The version of the script, always 1.
--- "lnum" The line number in "sid", zero if unknown.
--- "nowait" Do not wait for other, longer mappings.
--- (|:map-<nowait>|).
+--- "abbr" True if this is an |abbreviation|.
+--- "mode_bits" Nvim's internal binary representation of "mode".
+--- |mapset()| ignores this; only "mode" is used.
+--- See |maplist()| for usage examples. The values
+--- are from src/nvim/vim.h and may change in the
+--- future.
---
--- The dictionary can be used to restore a mapping with
--- |mapset()|.
@@ -5099,6 +5107,39 @@ function vim.fn.maparg(name, mode, abbr, dict) end
--- @return any
function vim.fn.mapcheck(name, mode, abbr) end
+--- Returns a |List| of all mappings. Each List item is a |Dict|,
+--- the same as what is returned by |maparg()|, see
+--- |mapping-dict|. When {abbr} is there and it is |TRUE| use
+--- abbreviations instead of mappings.
+---
+--- Example to show all mappings with "MultiMatch" in rhs: >vim
+--- echo maplist()->filter({_, m ->
+--- \ match(get(m, 'rhs', ''), 'MultiMatch') >= 0
+--- \ })
+--- <It can be tricky to find mappings for particular |:map-modes|.
+--- |mapping-dict|'s "mode_bits" can simplify this. For example,
+--- the mode_bits for Normal, Insert or Command-line modes are
+--- 0x19. To find all the mappings available in those modes you
+--- can do: >vim
+--- let saved_maps = []
+--- for m in maplist()
+--- if and(m.mode_bits, 0x19) != 0
+--- eval saved_maps->add(m)
+--- endif
+--- endfor
+--- echo saved_maps->mapnew({_, m -> m.lhs})
+--- <The values of the mode_bits are defined in Nvim's
+--- src/nvim/vim.h file and they can be discovered at runtime
+--- using |:map-commands| and "maplist()". Example: >vim
+--- omap xyzzy <Nop>
+--- let op_bit = maplist()->filter(
+--- \ {_, m -> m.lhs == 'xyzzy'})[0].mode_bits
+--- ounmap xyzzy
+--- echo printf("Operator-pending mode bit: 0x%x", op_bit)
+---
+--- @return any
+function vim.fn.maplist() end
+
--- Like |map()| but instead of replacing items in {expr1} a new
--- List or Dictionary is created and returned. {expr1} remains
--- unchanged. Items can still be changed by {expr2}, if you
@@ -5109,9 +5150,17 @@ function vim.fn.mapcheck(name, mode, abbr) end
--- @return any
function vim.fn.mapnew(expr1, expr2) end
---- Restore a mapping from a dictionary returned by |maparg()|.
---- {mode} and {abbr} should be the same as for the call to
---- |maparg()|. *E460*
+--- Restore a mapping from a dictionary, possibly returned by
+--- |maparg()| or |maplist()|. A buffer mapping, when dict.buffer
+--- is true, is set on the current buffer; it is up to the caller
+--- to ensure 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,
--- not the "mode" entry in {dict}.
--- Example for saving and restoring a mapping: >vim
@@ -5120,12 +5169,25 @@ function vim.fn.mapnew(expr1, expr2) end
--- " ...
--- 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 differ.
+--- e.g. with `:map!`, you need to save/restore the mapping for
+--- 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: >vim
+--- let save_maps = maplist()->filter(
+--- \ {_, m -> m.lhs == 'K'})
+--- nnoremap K somethingelse
+--- cnoremap K somethingelse2
+--- " ...
+--- unmap K
+--- for d in save_maps
+--- call mapset(d)
+--- endfor
---
--- @param mode string
---- @param abbr any
---- @param dict any
+--- @param abbr? any
+--- @param dict? any
--- @return any
function vim.fn.mapset(mode, abbr, dict) end
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index 73fbc8c922..8beca8ffb3 100644
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -1116,8 +1116,7 @@ func s:DeleteCommands()
if exists('s:k_map_saved')
if !empty(s:k_map_saved) && !s:k_map_saved.buffer
nunmap K
- " call mapset(s:k_map_saved)
- call mapset('n', 0, s:k_map_saved)
+ call mapset(s:k_map_saved)
elseif empty(s:k_map_saved)
nunmap K
endif
@@ -1126,8 +1125,7 @@ func s:DeleteCommands()
if exists('s:plus_map_saved')
if !empty(s:plus_map_saved) && !s:plus_map_saved.buffer
nunmap +
- " call mapset(s:plus_map_saved)
- call mapset('n', 0, s:plus_map_saved)
+ call mapset(s:plus_map_saved)
elseif empty(s:plus_map_saved)
nunmap +
endif
@@ -1136,8 +1134,7 @@ func s:DeleteCommands()
if exists('s:minus_map_saved')
if !empty(s:minus_map_saved) && !s:minus_map_saved.buffer
nunmap -
- " call mapset(s:minus_map_saved)
- call mapset('n', 0, s:minus_map_saved)
+ call mapset(s:minus_map_saved)
elseif empty(s:minus_map_saved)
nunmap -
endif
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 221be492af..a2ba0f0189 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -6142,7 +6142,8 @@ M.funcs = {
When {dict} is omitted or zero: Return the rhs of mapping
{name} in mode {mode}. The returned String has special
characters translated like in the output of the ":map" command
- listing.
+ listing. When {dict} is TRUE a dictionary is returned, see
+ below. To get a list of all mappings see |maplist()|.
When there is no mapping for {name}, an empty String is
returned if {dict} is FALSE, otherwise returns an empty Dict.
@@ -6170,7 +6171,7 @@ M.funcs = {
When {dict} is there and it is |TRUE| return a dictionary
containing all the information of the mapping with the
- following items:
+ following items: *mapping-dict*
"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
@@ -6189,9 +6190,16 @@ M.funcs = {
(|mapmode-ic|)
"sid" The script local ID, used for <sid> mappings
(|<SID>|). Negative for special contexts.
+ "scriptversion" The version of the script, always 1.
"lnum" The line number in "sid", zero if unknown.
"nowait" Do not wait for other, longer mappings.
(|:map-<nowait>|).
+ "abbr" True if this is an |abbreviation|.
+ "mode_bits" Nvim's internal binary representation of "mode".
+ |mapset()| ignores this; only "mode" is used.
+ See |maplist()| for usage examples. The values
+ are from src/nvim/vim.h and may change in the
+ future.
The dictionary can be used to restore a mapping with
|mapset()|.
@@ -6254,6 +6262,43 @@ M.funcs = {
params = { { 'name', 'string' }, { 'mode', 'string' }, { 'abbr', 'any' } },
signature = 'mapcheck({name} [, {mode} [, {abbr}]])',
},
+ maplist = {
+ args = { 0, 1 },
+ desc = [[
+ Returns a |List| of all mappings. Each List item is a |Dict|,
+ the same as what is returned by |maparg()|, see
+ |mapping-dict|. When {abbr} is there and it is |TRUE| use
+ abbreviations instead of mappings.
+
+ Example to show all mappings with "MultiMatch" in rhs: >vim
+ echo maplist()->filter({_, m ->
+ \ match(get(m, 'rhs', ''), 'MultiMatch') >= 0
+ \ })
+ <It can be tricky to find mappings for particular |:map-modes|.
+ |mapping-dict|'s "mode_bits" can simplify this. For example,
+ the mode_bits for Normal, Insert or Command-line modes are
+ 0x19. To find all the mappings available in those modes you
+ can do: >vim
+ let saved_maps = []
+ for m in maplist()
+ if and(m.mode_bits, 0x19) != 0
+ eval saved_maps->add(m)
+ endif
+ endfor
+ echo saved_maps->mapnew({_, m -> m.lhs})
+ <The values of the mode_bits are defined in Nvim's
+ src/nvim/vim.h file and they can be discovered at runtime
+ using |:map-commands| and "maplist()". Example: >vim
+ omap xyzzy <Nop>
+ let op_bit = maplist()->filter(
+ \ {_, m -> m.lhs == 'xyzzy'})[0].mode_bits
+ ounmap xyzzy
+ echo printf("Operator-pending mode bit: 0x%x", op_bit)
+ ]],
+ name = 'maplist',
+ params = {},
+ signature = 'maplist([{abbr}])'
+ },
mapnew = {
args = 2,
base = 1,
@@ -6268,12 +6313,20 @@ M.funcs = {
signature = 'mapnew({expr1}, {expr2})',
},
mapset = {
- args = 3,
+ args = { 1, 3 },
base = 1,
desc = [=[
- Restore a mapping from a dictionary returned by |maparg()|.
- {mode} and {abbr} should be the same as for the call to
- |maparg()|. *E460*
+ Restore a mapping from a dictionary, possibly returned by
+ |maparg()| or |maplist()|. A buffer mapping, when dict.buffer
+ is true, is set on the current buffer; it is up to the caller
+ to ensure 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,
not the "mode" entry in {dict}.
Example for saving and restoring a mapping: >vim
@@ -6282,8 +6335,21 @@ M.funcs = {
" ...
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 differ.
+ e.g. with `:map!`, you need to save/restore the mapping for
+ 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: >vim
+ let save_maps = maplist()->filter(
+ \ {_, m -> m.lhs == 'K'})
+ nnoremap K somethingelse
+ cnoremap K somethingelse2
+ " ...
+ unmap K
+ for d in save_maps
+ call mapset(d)
+ endfor
]=],
name = 'mapset',
params = { { 'mode', 'string' }, { 'abbr', 'any' }, { 'dict', 'any' } },
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index 4435e91993..2ee1e2e02d 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -123,6 +123,8 @@ static const char e_mapping_already_exists_for_str[]
= N_("E227: Mapping already exists for %s");
static const char e_entries_missing_in_mapset_dict_argument[]
= N_("E460: Entries missing in mapset() dict argument");
+static const char e_illegal_map_mode_string_str[]
+ = N_("E1276: Illegal map mode string: '%s'");
/// Get the start of the hashed map list for "state" and first character "c".
mapblock_T *get_maphash_list(int state, int c)
@@ -2070,11 +2072,12 @@ void f_hasmapto(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
///
/// @param mp The maphash that contains the mapping information
/// @param buffer_value The "buffer" value
+/// @param abbr True if abbreviation
/// @param compatible True for compatible with old maparg() dict
///
/// @return A Dictionary.
static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhsrawalt,
- const int buffer_value, const bool compatible)
+ const int buffer_value, const bool abbr, const bool compatible)
FUNC_ATTR_NONNULL_ARG(1)
{
Dictionary dict = ARRAY_DICT_INIT;
@@ -2113,6 +2116,7 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs
PUT(dict, "expr", INTEGER_OBJ(mp->m_expr ? 1 : 0));
PUT(dict, "silent", INTEGER_OBJ(mp->m_silent ? 1 : 0));
PUT(dict, "sid", INTEGER_OBJ(mp->m_script_ctx.sc_sid));
+ PUT(dict, "scriptversion", INTEGER_OBJ(1));
PUT(dict, "lnum", INTEGER_OBJ(mp->m_script_ctx.sc_lnum));
PUT(dict, "buffer", INTEGER_OBJ(buffer_value));
PUT(dict, "nowait", INTEGER_OBJ(mp->m_nowait ? 1 : 0));
@@ -2120,6 +2124,8 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs
PUT(dict, "replace_keycodes", INTEGER_OBJ(1));
}
PUT(dict, "mode", CSTR_AS_OBJ(mapmode));
+ PUT(dict, "abbr", INTEGER_OBJ(abbr ? 1 : 0));
+ PUT(dict, "mode_bits", INTEGER_OBJ(mp->m_mode));
return dict;
}
@@ -2192,7 +2198,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) {
Dictionary dict = mapblock_fill_dict(mp,
did_simplify ? keys_simplified : NULL,
- buffer_local, true);
+ buffer_local, abbr, true);
(void)object_to_vim(DICTIONARY_OBJ(dict), rettv, NULL);
api_free_dictionary(dict);
} else {
@@ -2205,21 +2211,99 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
xfree(alt_keys_buf);
}
+/// 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(const char *const mode_string, const bool abbr)
+{
+ const char *p = mode_string;
+ const int MASK_V = MODE_VISUAL | MODE_SELECT;
+ const int MASK_MAP = MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING;
+ const int MASK_BANG = MODE_INSERT | MODE_CMDLINE;
+
+ if (*p == NUL) {
+ p = " "; // compatibility
+ }
+ int mode = 0;
+ int modec;
+ while ((modec = (uint8_t)(*p++))) {
+ int tmode;
+ switch (modec) {
+ case 'i':
+ tmode = MODE_INSERT; break;
+ case 'l':
+ tmode = MODE_LANGMAP; break;
+ case 'c':
+ tmode = MODE_CMDLINE; break;
+ case 'n':
+ tmode = MODE_NORMAL; break;
+ case 'x':
+ tmode = MODE_VISUAL; break;
+ case 's':
+ tmode = MODE_SELECT; break;
+ case 'o':
+ tmode = MODE_OP_PENDING; break;
+ case 't':
+ tmode = MODE_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
void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
+ const char *which;
char buf[NUMBUFLEN];
- const char *which = tv_get_string_buf_chk(&argvars[0], buf);
- if (which == NULL) {
- return;
+ int is_abbr;
+ dict_T *d;
+
+ // If first arg is a dict, then that's the only arg permitted.
+ const bool dict_only = argvars[0].v_type == VAR_DICT;
+
+ if (dict_only) {
+ d = argvars[0].vval.v_dict;
+ which = tv_dict_get_string(d, "mode", false);
+ is_abbr = (int)tv_dict_get_bool(d, "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);
+ if (which == NULL) {
+ return;
+ }
+ is_abbr = (int)tv_get_bool(&argvars[1]);
+ if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
+ return;
+ }
+ d = argvars[2].vval.v_dict;
}
- const int mode = get_map_mode((char **)&which, 0);
- const bool is_abbr = tv_get_number(&argvars[1]) != 0;
-
- if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
+ const int mode = get_map_mode_string(which, is_abbr);
+ if (mode == 0) {
+ semsg(_(e_illegal_map_mode_string_str), which);
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);
@@ -2280,6 +2364,59 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
sid, lnum, false);
}
+/// "maplist()" function
+void f_maplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
+ const bool abbr = argvars[0].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[0]);
+
+ tv_list_alloc_ret(rettv, kListLenUnknown);
+
+ // Do it twice: once for global maps and once for local maps.
+ for (int buffer_local = 0; buffer_local <= 1; buffer_local++) {
+ for (int hash = 0; hash < 256; hash++) {
+ mapblock_T *mp;
+ if (abbr) {
+ if (hash > 0) { // there is only one abbr list
+ break;
+ }
+ if (buffer_local) {
+ mp = curbuf->b_first_abbr;
+ } else {
+ mp = first_abbr;
+ }
+ } else if (buffer_local) {
+ mp = curbuf->b_maphash[hash];
+ } else {
+ mp = maphash[hash];
+ }
+ for (; mp; mp = mp->m_next) {
+ if (mp->m_simplified) {
+ continue;
+ }
+
+ char *keys_buf = NULL;
+ bool did_simplify = false;
+
+ char *lhs = str2special_save(mp->m_keys, true, false);
+ (void)replace_termcodes(lhs, strlen(lhs), &keys_buf, 0, flags, &did_simplify,
+ CPO_TO_CPO_FLAGS);
+ xfree(lhs);
+
+ Dictionary dict = mapblock_fill_dict(mp,
+ did_simplify ? keys_buf : NULL,
+ buffer_local, abbr, true);
+ typval_T d = TV_INITIAL_VALUE;
+ (void)object_to_vim(DICTIONARY_OBJ(dict), &d, NULL);
+ assert(d.v_type == VAR_DICT);
+ tv_list_append_dict(rettv->vval.v_list, d.vval.v_dict);
+ api_free_dictionary(dict);
+ xfree(keys_buf);
+ }
+ }
+ }
+}
+
/// "maparg()" function
void f_maparg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -2697,7 +2834,8 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf)
// Check for correct mode
if (int_mode & current_maphash->m_mode) {
ADD(mappings,
- DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL, buffer_value, false)));
+ DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL,
+ buffer_value, false, false)));
}
}
}
diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua
index 811c4210fa..3af1faaece 100644
--- a/test/functional/api/keymap_spec.lua
+++ b/test/functional/api/keymap_spec.lua
@@ -19,6 +19,20 @@ local sleep = helpers.sleep
local sid_api_client = -9
local sid_lua = -8
+local mode_bits_map = {
+ ['n'] = 0x01,
+ ['x'] = 0x02,
+ ['o'] = 0x04,
+ ['c'] = 0x08,
+ ['i'] = 0x10,
+ ['l'] = 0x20,
+ ['s'] = 0x40,
+ ['t'] = 0x80,
+ [' '] = 0x47,
+ ['v'] = 0x42,
+ ['!'] = 0x18,
+}
+
describe('nvim_get_keymap', function()
before_each(clear)
@@ -32,9 +46,12 @@ describe('nvim_get_keymap', function()
rhs='bar',
expr=0,
sid=0,
+ scriptversion=1,
buffer=0,
nowait=0,
mode='n',
+ mode_bits=0x01,
+ abbr=0,
noremap=1,
lnum=0,
}
@@ -81,6 +98,7 @@ describe('nvim_get_keymap', function()
-- The table will be the same except for the mode
local insert_table = shallowcopy(foo_bar_map_table)
insert_table['mode'] = 'i'
+ insert_table['mode_bits'] = 0x10
eq({insert_table}, meths.get_keymap('i'))
end)
@@ -258,8 +276,10 @@ describe('nvim_get_keymap', function()
silent=0,
expr=0,
sid=0,
+ scriptversion=1,
buffer=0,
nowait=0,
+ abbr=0,
noremap=1,
lnum=0,
}
@@ -268,6 +288,7 @@ describe('nvim_get_keymap', function()
ret.lhs = lhs
ret.rhs = rhs
ret.mode = mode
+ ret.mode_bits = mode_bits_map[mode]
return ret
end
@@ -323,10 +344,13 @@ describe('nvim_get_keymap', function()
lhsraw='| |',
rhs='| |',
mode='n',
+ mode_bits=0x01,
+ abbr=0,
script=0,
silent=0,
expr=0,
sid=0,
+ scriptversion=1,
buffer=0,
nowait=0,
noremap=1,
@@ -365,15 +389,18 @@ describe('nvim_get_keymap', function()
silent=0,
expr=0,
sid=sid_lua,
+ scriptversion=1,
buffer=0,
nowait=0,
mode='n',
+ mode_bits=0x01,
+ abbr=0,
noremap=0,
lnum=0,
}, mapargs[1])
end)
- it ('can handle map descriptions', function()
+ it('can handle map descriptions', function()
meths.set_keymap('n', 'lhs', 'rhs', {desc="map description"})
eq({
lhs='lhs',
@@ -383,9 +410,12 @@ describe('nvim_get_keymap', function()
silent=0,
expr=0,
sid=sid_api_client,
+ scriptversion=1,
buffer=0,
nowait=0,
mode='n',
+ mode_bits=0x01,
+ abbr=0,
noremap=0,
lnum=0,
desc='map description'
@@ -420,7 +450,10 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
end
local to_return = {}
- to_return.mode = normalize_mapmode(mode, true)
+ local expected_mode = normalize_mapmode(mode, true)
+ to_return.mode = expected_mode
+ to_return.mode_bits = mode_bits_map[expected_mode]
+ to_return.abbr = mode:sub(-1) == 'a' and 1 or 0
to_return.noremap = not opts.noremap and 0 or 1
to_return.lhs = lhs
to_return.rhs = rhs
@@ -429,6 +462,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
to_return.nowait = not opts.nowait and 0 or 1
to_return.expr = not opts.expr and 0 or 1
to_return.sid = not opts.sid and sid_api_client or opts.sid
+ to_return.scriptversion = 1
to_return.buffer = not opts.buffer and 0 or opts.buffer
to_return.lnum = not opts.lnum and 0 or opts.lnum
to_return.desc = opts.desc
@@ -487,7 +521,12 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
get_mapargs('', 'lhs'))
end)
- it('throws errors when given too-long mode shortnames', function()
+ it('error on invalid mode shortname', function()
+ eq('Invalid mode shortname: " "', pcall_err(meths.set_keymap, ' ', 'lhs', 'rhs', {}))
+ eq('Invalid mode shortname: "m"', pcall_err(meths.set_keymap, 'm', 'lhs', 'rhs', {}))
+ eq('Invalid mode shortname: "?"', pcall_err(meths.set_keymap, '?', 'lhs', 'rhs', {}))
+ eq('Invalid mode shortname: "y"', pcall_err(meths.set_keymap, 'y', 'lhs', 'rhs', {}))
+ eq('Invalid mode shortname: "p"', pcall_err(meths.set_keymap, 'p', 'lhs', 'rhs', {}))
eq('Invalid mode shortname: "a"', pcall_err(meths.set_keymap, 'a', 'lhs', 'rhs', {}))
eq('Invalid mode shortname: "oa"', pcall_err(meths.set_keymap, 'oa', 'lhs', 'rhs', {}))
eq('Invalid mode shortname: "!o"', pcall_err(meths.set_keymap, '!o', 'lhs', 'rhs', {}))
@@ -496,6 +535,11 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
eq('Invalid mode shortname: "map"', pcall_err(meths.set_keymap, 'map', 'lhs', 'rhs', {}))
eq('Invalid mode shortname: "vmap"', pcall_err(meths.set_keymap, 'vmap', 'lhs', 'rhs', {}))
eq('Invalid mode shortname: "xnoremap"', pcall_err(meths.set_keymap, 'xnoremap', 'lhs', 'rhs', {}))
+ eq('Invalid mode shortname: " "', pcall_err(meths.del_keymap, ' ', 'lhs'))
+ eq('Invalid mode shortname: "m"', pcall_err(meths.del_keymap, 'm', 'lhs'))
+ eq('Invalid mode shortname: "?"', pcall_err(meths.del_keymap, '?', 'lhs'))
+ eq('Invalid mode shortname: "y"', pcall_err(meths.del_keymap, 'y', 'lhs'))
+ eq('Invalid mode shortname: "p"', pcall_err(meths.del_keymap, 'p', 'lhs'))
eq('Invalid mode shortname: "a"', pcall_err(meths.del_keymap, 'a', 'lhs'))
eq('Invalid mode shortname: "oa"', pcall_err(meths.del_keymap, 'oa', 'lhs'))
eq('Invalid mode shortname: "!o"', pcall_err(meths.del_keymap, '!o', 'lhs'))
@@ -506,22 +550,6 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
eq('Invalid mode shortname: "xnoremap"', pcall_err(meths.del_keymap, 'xnoremap', 'lhs'))
end)
- it('error on invalid mode shortname', function()
- eq('Invalid mode shortname: " "',
- pcall_err(meths.set_keymap, ' ', 'lhs', 'rhs', {}))
- eq('Invalid mode shortname: "m"',
- pcall_err(meths.set_keymap, 'm', 'lhs', 'rhs', {}))
- eq('Invalid mode shortname: "?"',
- pcall_err(meths.set_keymap, '?', 'lhs', 'rhs', {}))
- eq('Invalid mode shortname: "y"',
- pcall_err(meths.set_keymap, 'y', 'lhs', 'rhs', {}))
- eq('Invalid mode shortname: "p"',
- pcall_err(meths.set_keymap, 'p', 'lhs', 'rhs', {}))
- eq('Invalid mode shortname: "?"', pcall_err(meths.del_keymap, '?', 'lhs'))
- eq('Invalid mode shortname: "y"', pcall_err(meths.del_keymap, 'y', 'lhs'))
- eq('Invalid mode shortname: "p"', pcall_err(meths.del_keymap, 'p', 'lhs'))
- end)
-
it('error on invalid optnames', function()
eq("Invalid key: 'silentt'",
pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {silentt = true}))
@@ -827,7 +855,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
eq(1, exec_lua[[return GlobalCount]])
end)
- it (':map command shows lua mapping correctly', function()
+ it(':map command shows lua mapping correctly', function()
exec_lua [[
vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() print('jkl;') end })
]]
@@ -839,7 +867,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
)
end)
- it ('mapcheck() returns lua mapping correctly', function()
+ it('mapcheck() returns lua mapping correctly', function()
exec_lua [[
vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() print('jkl;') end })
]]
@@ -847,7 +875,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
"^<Lua %d+>"))
end)
- it ('maparg() returns lua mapping correctly', function()
+ it('maparg() returns lua mapping correctly', function()
eq(0, exec_lua([[
GlobalCount = 0
vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
diff --git a/test/functional/vimscript/map_functions_spec.lua b/test/functional/vimscript/map_functions_spec.lua
index 2c8fe69428..acba5e9708 100644
--- a/test/functional/vimscript/map_functions_spec.lua
+++ b/test/functional/vimscript/map_functions_spec.lua
@@ -26,9 +26,12 @@ describe('maparg()', function()
rhs='bar',
expr=0,
sid=0,
+ scriptversion=1,
buffer=0,
nowait=0,
mode='n',
+ mode_bits=0x01,
+ abbr=0,
noremap=1,
lnum=0,
}
@@ -155,10 +158,13 @@ describe('maparg()', function()
buffer = 0,
expr = 0,
mode = 'n',
+ mode_bits = 0x01,
+ abbr = 0,
noremap = 1,
nowait = 0,
- script=0,
+ script = 0,
sid = 0,
+ scriptversion = 1,
silent = 0,
lnum = 0,
}
diff --git a/test/old/testdir/test_maparg.vim b/test/old/testdir/test_map_functions.vim
index 1837511990..2aeb470a0d 100644
--- a/test/old/testdir/test_maparg.vim
+++ b/test/old/testdir/test_map_functions.vim
@@ -1,4 +1,4 @@
-" Tests for maparg(), mapcheck() and mapset().
+" Tests for maparg(), mapcheck(), mapset(), maplist()
" Also test utf8 map with a 0x80 byte.
source shared.vim
@@ -19,28 +19,41 @@ func Test_maparg()
call assert_equal("is<F4>foo", maparg('foo<C-V>'))
call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo<C-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},
+ \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1,
+ \ 'lnum': lnum + 1,
+ \ 'rhs': 'is<F4>foo', 'buffer': 0, 'abbr': 0, 'mode_bits': 0x47},
\ 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},
+ \ 'nowait': 0, 'expr': 1, 'sid': sid, 'scriptversion': 1,
+ \ 'lnum': lnum + 2,
+ \ 'rhs': 'isbar', 'buffer': 1, 'abbr': 0, 'mode_bits': 0x42},
\ 'bar'->maparg('', 0, 1))
let lnum = expand('<sflnum>')
map <buffer> <nowait> foo bar
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},
+ \ 'nowait': 1, 'expr': 0, 'sid': sid, 'scriptversion': 1,
+ \ 'lnum': lnum + 1, 'rhs': 'bar',
+ \ 'buffer': 1, 'abbr': 0, 'mode_bits': 0x47},
\ maparg('foo', '', 0, 1))
let lnum = expand('<sflnum>')
tmap baz foo
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},
+ \ 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1,
+ \ 'lnum': lnum + 1, 'rhs': 'foo',
+ \ 'buffer': 0, 'abbr': 0, 'mode_bits': 0x80},
\ 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, 'mode_bits': 0x0010},
+ \ maparg('A', 'i', 1, 1))
+ iuna A
map abc x<char-114>x
call assert_equal("xrx", maparg('abc'))
@@ -274,6 +287,152 @@ func Test_mapset()
call assert_fails('call mapset("i", 0, {})', 'E460:')
endfunc
+func 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
+ func s:GetModes(abbr = v:false)
+ return maplist(a:abbr)->filter({_, m -> m.lhs == 'XZ'})
+ \ ->mapnew({_, m -> m.mode})
+ endfunc
+
+ func s:UnmapAll(lhs)
+ const unmap_cmds = [ 'unmap', 'unmap!', 'tunmap', 'lunmap' ]
+ for cmd in unmap_cmds
+ try | call execute(cmd .. ' ' .. a:lhs) | catch /E31/ | endtry
+ endfor
+ endfunc
+
+ let tmap = {}
+
+ " some mapset(mode, abbr, dict) tests using get_map_mode_str
+ map XZ x
+ let tmap = maplist()->filter({_, m -> m.lhs == 'XZ'})[0]->copy()
+ " this splits the mapping into 2 mappings
+ call mapset('ox', v:false, tmap)
+ call assert_equal(2, len(s:GetModes()))
+ call mapset('o', v:false, tmap)
+ call assert_equal(3, len(s:GetModes()))
+ " test that '' acts like ' ', and that the 3 mappings become 1
+ call mapset('', v:false, tmap)
+ call assert_equal([' '], s:GetModes())
+ " dict's mode/abbr are ignored
+ call s:UnmapAll('XZ')
+ let tmap.mode = '!'
+ let tmap.abbr = v:true
+ call mapset('o', v:false, tmap)
+ call assert_equal(['o'], s:GetModes())
+
+ " test the 3 arg version handles bad mode string, dict not used
+ call assert_fails("call mapset('vi', v:false, {})", 'E1276:')
+
+
+ " get the abbreviations out of the way
+ abbreviate XZ ZX
+ let tmap = maplist(v:true)->filter({_, m -> m.lhs == 'XZ'})[0]->copy()
+
+ abclear
+ " 'ic' is the default ab command, shows up as '!'
+ let tmap.mode = 'ic'
+ call mapset(tmap)
+ call assert_equal(['!'], s:GetModes(v:true))
+
+ abclear
+ let tmap.mode = 'i'
+ call mapset(tmap)
+ call assert_equal(['i'], s:GetModes(v:true))
+
+ abclear
+ let tmap.mode = 'c'
+ call mapset(tmap)
+ call assert_equal(['c'], s:GetModes(v:true))
+
+ abclear
+ let tmap.mode = '!'
+ call mapset(tmap)
+ call assert_equal(['!'], s:GetModes(v:true))
+
+ call assert_fails("call mapset(#{mode: ' !', abbr: 1})", 'E1276:')
+ call assert_fails("call mapset(#{mode: 'cl', abbr: 1})", 'E1276:')
+ call assert_fails("call mapset(#{mode: 'in', abbr: 1})", 'E1276:')
+
+ " the map commands
+ map XZ x
+ let tmap = maplist()->filter({_, m -> m.lhs == 'XZ'})[0]->copy()
+
+ " try the combos
+ call s:UnmapAll('XZ')
+ " 'nxso' is ' ', the unadorned :map
+ let tmap.mode = 'nxso'
+ call mapset(tmap)
+ call assert_equal([' '], s:GetModes())
+
+ cal s:UnmapAll('XZ')
+ " 'ic' is '!'
+ let tmap.mode = 'ic'
+ call mapset(tmap)
+ call assert_equal(['!'], s:GetModes())
+
+ call s:UnmapAll('XZ')
+ " 'xs' is really 'v'
+ let tmap.mode = 'xs'
+ call mapset(tmap)
+ call assert_equal(['v'], s:GetModes())
+
+ " try the individual modes
+ call s:UnmapAll('XZ')
+ let tmap.mode = 'n'
+ call mapset(tmap)
+ call assert_equal(['n'], s:GetModes())
+
+ call s:UnmapAll('XZ')
+ let tmap.mode = 'x'
+ call mapset(tmap)
+ call assert_equal(['x'], s:GetModes())
+
+ call s:UnmapAll('XZ')
+ let tmap.mode = 's'
+ call mapset(tmap)
+ call assert_equal(['s'], s:GetModes())
+
+ call s:UnmapAll('XZ')
+ let tmap.mode = 'o'
+ call mapset(tmap)
+ call assert_equal(['o'], s:GetModes())
+
+ call s:UnmapAll('XZ')
+ let tmap.mode = 'i'
+ call mapset(tmap)
+ call assert_equal(['i'], s:GetModes())
+
+ call s:UnmapAll('XZ')
+ let tmap.mode = 'c'
+ call mapset(tmap)
+ call assert_equal(['c'], s:GetModes())
+
+ call s:UnmapAll('XZ')
+ let tmap.mode = 't'
+ call mapset(tmap)
+ call assert_equal(['t'], s:GetModes())
+
+ call s:UnmapAll('XZ')
+ let tmap.mode = 'l'
+ call mapset(tmap)
+ call assert_equal(['l'], s:GetModes())
+
+ call s:UnmapAll('XZ')
+
+ " get errors for modes that can't be in one mapping
+ call assert_fails("call mapset(#{mode: 'nxsoi', abbr: 0})", 'E1276:')
+ call assert_fails("call mapset(#{mode: ' !', abbr: 0})", 'E1276:')
+ call assert_fails("call mapset(#{mode: 'ix', abbr: 0})", 'E1276:')
+ call assert_fails("call mapset(#{mode: 'tl', abbr: 0})", 'E1276:')
+ call assert_fails("call mapset(#{mode: ' l', abbr: 0})", 'E1276:')
+ call assert_fails("call mapset(#{mode: ' t', abbr: 0})", 'E1276:')
+endfunc
+
func Check_ctrlb_map(d, check_alt)
call assert_equal('<C-B>', a:d.lhs)
if a:check_alt
@@ -368,4 +527,92 @@ func Test_map_restore_negative_sid()
call delete('Xresult')
endfunc
+func Test_maplist()
+ new
+ func s:ClearMappingsAbbreviations()
+ mapclear | nmapclear | vmapclear | xmapclear | smapclear | omapclear
+ mapclear! | imapclear | lmapclear | cmapclear | tmapclear
+ mapclear <buffer> | nmapclear <buffer> | vmapclear <buffer>
+ xmapclear <buffer> | smapclear <buffer> | omapclear <buffer>
+ mapclear! <buffer> | imapclear <buffer> | lmapclear <buffer>
+ cmapclear <buffer> | tmapclear <buffer>
+ abclear | abclear <buffer>
+ endfunc
+
+ func s:AddMaps(new, accum)
+ if len(a:new) > 0 && a:new[0] != "No mapping found"
+ eval a:accum->extend(a:new)
+ endif
+ endfunc
+
+ call s:ClearMappingsAbbreviations()
+ call assert_equal(0, len(maplist()))
+ call assert_equal(0, len(maplist(v:true)))
+
+ " Set up some mappings.
+ map dup bar
+ map <buffer> dup bufbar
+ map foo<C-V> is<F4>foo
+ vnoremap <script> <buffer> <expr> <silent> bar isbar
+ tmap baz foo
+ omap h w
+ lmap i w
+ nmap j w
+ xmap k w
+ smap l w
+ map abc <Nop>
+ nmap <M-j> x
+ nmap <M-Space> y
+ " And abbreviations
+ abbreviate xy he
+ abbreviate xx she
+ abbreviate <buffer> x they
+
+ " Get a list of the mappings with the ':map' commands.
+ " Check maplist() return a list of the same size.
+ call assert_equal(13, len(maplist()))
+ call assert_equal(3, len(maplist(v:true)))
+ call assert_equal(13, len(maplist(v:false)))
+
+ " collect all the current maps using :map commands
+ let maps_command = []
+ call s:AddMaps(split(execute('map'), '\n'), maps_command)
+ call s:AddMaps(split(execute('map!'), '\n'), maps_command)
+ call s:AddMaps(split(execute('tmap'), '\n'), maps_command)
+ call s:AddMaps(split(execute('lmap'), '\n'), maps_command)
+
+ " Use maplist to get all the maps
+ let maps_maplist = maplist()
+ call assert_equal(len(maps_command), len(maps_maplist))
+
+ " make sure all the mode-lhs are unique, no duplicates
+ let map_set = {}
+ for d in maps_maplist
+ let map_set[d.mode .. "-" .. d.lhs .. "-" .. d.buffer] = 0
+ endfor
+ call assert_equal(len(maps_maplist), len(map_set))
+
+ " For everything returned by maplist, should be the same as from maparg.
+ " Except for "map dup", bacause maparg returns the <buffer> version
+ for d in maps_maplist
+ if d.lhs == 'dup' && d.buffer == 0
+ continue
+ endif
+ let d_maparg = maparg(d.lhs, d.mode, v:false, v:true)
+ call assert_equal(d_maparg, d)
+ endfor
+
+ " Check abbr matches maparg
+ for d in maplist(v:true)
+ " Note, d.mode is '!', but can't use that with maparg
+ let d_maparg = maparg(d.lhs, 'i', v:true, v:true)
+ call assert_equal(d_maparg, d)
+ endfor
+
+ call s:ClearMappingsAbbreviations()
+ call assert_equal(0, len(maplist()))
+ call assert_equal(0, len(maplist(v:true)))
+endfunc
+
+
" vim: shiftwidth=2 sts=2 expandtab