diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 1eb173af58..bc4d1f30c9 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -4186,6 +4186,11 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()* "nowait" Do not wait for other, longer mappings. (|:map-|). "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()|. @@ -4239,6 +4244,26 @@ maplist([{abbr}]) *maplist()* 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 + 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 diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index dea2e476b6..d1e676ef70 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -5047,6 +5047,11 @@ function vim.fn.map(expr1, expr2) end --- "nowait" Do not wait for other, longer mappings. --- (|:map-|). --- "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()|. @@ -5111,6 +5116,26 @@ function vim.fn.mapcheck(name, mode, abbr) end --- echo maplist()->filter({_, m -> --- \ match(get(m, 'rhs', ''), 'MultiMatch') >= 0 --- \ }) +--- 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}) +--- vim +--- omap xyzzy +--- 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 diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index d65b584faa..a2ba0f0189 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -6195,6 +6195,11 @@ M.funcs = { "nowait" Do not wait for other, longer mappings. (|:map-|). "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()|. @@ -6269,6 +6274,26 @@ M.funcs = { echo maplist()->filter({_, m -> \ match(get(m, 'rhs', ''), 'MultiMatch') >= 0 \ }) + 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}) + vim + omap xyzzy + 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 = {}, diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index c90c0c5529..2ee1e2e02d 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -2125,6 +2125,7 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs } 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; } diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index f105a31ed9..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) @@ -36,6 +50,7 @@ describe('nvim_get_keymap', function() buffer=0, nowait=0, mode='n', + mode_bits=0x01, abbr=0, noremap=1, lnum=0, @@ -83,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) @@ -272,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 @@ -327,6 +344,7 @@ describe('nvim_get_keymap', function() lhsraw='| |', rhs='| |', mode='n', + mode_bits=0x01, abbr=0, script=0, silent=0, @@ -375,6 +393,7 @@ describe('nvim_get_keymap', function() buffer=0, nowait=0, mode='n', + mode_bits=0x01, abbr=0, noremap=0, lnum=0, @@ -395,6 +414,7 @@ describe('nvim_get_keymap', function() buffer=0, nowait=0, mode='n', + mode_bits=0x01, abbr=0, noremap=0, lnum=0, @@ -430,7 +450,9 @@ 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 @@ -499,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', {})) @@ -508,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')) @@ -518,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})) @@ -839,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 }) ]] @@ -851,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 }) ]] @@ -859,7 +875,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() "^")) 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 14818d9141..acba5e9708 100644 --- a/test/functional/vimscript/map_functions_spec.lua +++ b/test/functional/vimscript/map_functions_spec.lua @@ -30,6 +30,7 @@ describe('maparg()', function() buffer=0, nowait=0, mode='n', + mode_bits=0x01, abbr=0, noremap=1, lnum=0, @@ -157,6 +158,7 @@ describe('maparg()', function() buffer = 0, expr = 0, mode = 'n', + mode_bits = 0x01, abbr = 0, noremap = 1, nowait = 0, diff --git a/test/old/testdir/test_map_functions.vim b/test/old/testdir/test_map_functions.vim index 7e3ff4c88d..2aeb470a0d 100644 --- a/test/old/testdir/test_map_functions.vim +++ b/test/old/testdir/test_map_functions.vim @@ -21,13 +21,13 @@ func Test_maparg() \ 'lhsraw': "foo\x80\xfc\x04V", 'lhsrawalt': "foo\x16", \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1, \ 'lnum': lnum + 1, - \ 'rhs': 'isfoo', 'buffer': 0, 'abbr': 0}, + \ 'rhs': 'isfoo', 'buffer': 0, 'abbr': 0, 'mode_bits': 0x47}, \ maparg('foo', '', 0, 1)) call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar', \ 'lhsraw': 'bar', 'mode': 'v', \ 'nowait': 0, 'expr': 1, 'sid': sid, 'scriptversion': 1, \ 'lnum': lnum + 2, - \ 'rhs': 'isbar', 'buffer': 1, 'abbr': 0}, + \ 'rhs': 'isbar', 'buffer': 1, 'abbr': 0, 'mode_bits': 0x42}, \ 'bar'->maparg('', 0, 1)) let lnum = expand('') map foo bar @@ -35,7 +35,7 @@ func Test_maparg() \ 'lhsraw': 'foo', 'mode': ' ', \ 'nowait': 1, 'expr': 0, 'sid': sid, 'scriptversion': 1, \ 'lnum': lnum + 1, 'rhs': 'bar', - \ 'buffer': 1, 'abbr': 0}, + \ 'buffer': 1, 'abbr': 0, 'mode_bits': 0x47}, \ maparg('foo', '', 0, 1)) let lnum = expand('') tmap baz foo @@ -43,7 +43,7 @@ func Test_maparg() \ 'lhsraw': 'baz', 'mode': 't', \ 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1, \ 'lnum': lnum + 1, 'rhs': 'foo', - \ 'buffer': 0, 'abbr': 0}, + \ 'buffer': 0, 'abbr': 0, 'mode_bits': 0x80}, \ maparg('baz', 't', 0, 1)) let lnum = expand('') iab A B @@ -51,7 +51,7 @@ func Test_maparg() \ 'lhsraw': 'A', 'mode': 'i', \ 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1, \ 'lnum': lnum + 1, 'rhs': 'B', - \ 'buffer': 0, 'abbr': 1}, + \ 'buffer': 0, 'abbr': 1, 'mode_bits': 0x0010}, \ maparg('A', 'i', 1, 1)) iuna A