mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	feat(lua): add vim.keymap
This introduces two new functions `vim.keymap.set` & `vim.keymap.del`
differences compared to regular set_keymap:
- remap is used as opposite of noremap. By default it's true for <Plug> keymaps and false for others.
- rhs can be lua function.
- mode can be a list of modes.
- replace_keycodes option for lua function expr maps. (Default: true)
- handles buffer specific keymaps
Examples:
```lua
vim.keymap.set('n', 'asdf', function() print("real lua function") end)
vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, {buffer=true})
vim.keymap.set('n', '<leader>w', "<cmd>w<cr>", {silent = true, buffer = 5 })
vim.keymap.set('i', '<Tab>', function()
  return vim.fn.pumvisible() == 1 and "<C-n>" or "<Tab>"
end, {expr = true})
vim.keymap.set('n', '[%', '<Plug>(MatchitNormalMultiBackward)')
vim.keymap.del('n', 'asdf')
vim.keymap.del({'n', 'i', 'v'}, '<leader>w', {buffer = 5 })
```
			
			
This commit is contained in:
		| @@ -1847,4 +1847,81 @@ add({filetypes})                                          *vim.filetype.add()* | |||||||
|                     {filetypes}  table A table containing new filetype maps |                     {filetypes}  table A table containing new filetype maps | ||||||
|                                  (see example). |                                  (see example). | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ============================================================================== | ||||||
|  | Lua module: keymap                                                *lua-keymap* | ||||||
|  |  | ||||||
|  | del({modes}, {lhs}, {opts})                                 *vim.keymap.del()* | ||||||
|  |                 Remove an existing mapping. Examples: > | ||||||
|  |  | ||||||
|  |                    vim.keymap.del('n', 'lhs') | ||||||
|  |  | ||||||
|  |                    vim.keymap.del({'n', 'i', 'v'}, '<leader>w', { buffer = 5 }) | ||||||
|  | < | ||||||
|  |  | ||||||
|  |                 Parameters: ~ | ||||||
|  |                     {opts}  table A table of optional arguments: | ||||||
|  |                             • buffer: (number or boolean) Remove a mapping | ||||||
|  |                               from the given buffer. When "true" or 0, use the | ||||||
|  |                               current buffer. | ||||||
|  |  | ||||||
|  |                 See also: ~ | ||||||
|  |                     |vim.keymap.set()| | ||||||
|  |  | ||||||
|  | set({mode}, {lhs}, {rhs}, {opts})                           *vim.keymap.set()* | ||||||
|  |                 Add a new |mapping|. Examples: > | ||||||
|  |  | ||||||
|  |                    -- Can add mapping to Lua functions | ||||||
|  |                    vim.keymap.set('n', 'lhs', function() print("real lua function") end) | ||||||
|  |  | ||||||
|  |                    -- Can use it to map multiple modes | ||||||
|  |                    vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer=true }) | ||||||
|  |  | ||||||
|  |                    -- Can add mapping for specific buffer | ||||||
|  |                    vim.keymap.set('n', '<leader>w', "<cmd>w<cr>", { silent = true, buffer = 5 }) | ||||||
|  |  | ||||||
|  |                    -- Expr mappings | ||||||
|  |                    vim.keymap.set('i', '<Tab>', function() | ||||||
|  |                      return vim.fn.pumvisible() == 1 and "<C-n>" or "<Tab>" | ||||||
|  |                    end, { expr = true }) | ||||||
|  |                    -- <Plug> mappings | ||||||
|  |                    vim.keymap.set('n', '[%', '<Plug>(MatchitNormalMultiBackward)') | ||||||
|  | < | ||||||
|  |  | ||||||
|  |                 Note that in a mapping like: > | ||||||
|  |  | ||||||
|  |                     vim.keymap.set('n', 'asdf', require('jkl').my_fun) | ||||||
|  | < | ||||||
|  |  | ||||||
|  |                 the require('jkl') gets evaluated during this call in order to | ||||||
|  |                 access the function. If you want to avoid this cost at startup | ||||||
|  |                 you can wrap it in a function, for example: > | ||||||
|  |  | ||||||
|  |                     vim.keymap.set('n', 'asdf', function() return require('jkl').my_fun() end) | ||||||
|  | < | ||||||
|  |  | ||||||
|  |                 Parameters: ~ | ||||||
|  |                     {mode}  string|table Same mode short names as | ||||||
|  |                             |nvim_set_keymap()|. Can also be list of modes to | ||||||
|  |                             create mapping on multiple modes. | ||||||
|  |                     {lhs}   string Left-hand side |{lhs}| of the mapping. | ||||||
|  |                     {rhs}   string|function Right-hand side |{rhs}| of the | ||||||
|  |                             mapping. Can also be a Lua function. | ||||||
|  |                     {opts}  table A table of |:map-arguments| such as | ||||||
|  |                             "silent". In addition to the options listed in | ||||||
|  |                             |nvim_set_keymap()|, this table also accepts the | ||||||
|  |                             following keys: | ||||||
|  |                             • replace_keycodes: (boolean, default true) When | ||||||
|  |                               both this and expr is "true", | ||||||
|  |                               |nvim_replace_termcodes()| is applied to the | ||||||
|  |                               result of Lua expr maps. | ||||||
|  |                             • remap: (boolean) Make the mapping recursive. | ||||||
|  |                               This is the inverse of the "noremap" option from | ||||||
|  |                               |nvim_set_keymap()|. Default `true` if `lhs` is | ||||||
|  |                               a string starting with `<plug>` | ||||||
|  |                               (case-insensitive), `false` otherwise. | ||||||
|  |  | ||||||
|  |                 See also: ~ | ||||||
|  |                     |nvim_set_keymap()| | ||||||
|  |  | ||||||
|  vim:tw=78:ts=8:ft=help:norl: |  vim:tw=78:ts=8:ft=help:norl: | ||||||
|   | |||||||
							
								
								
									
										135
									
								
								runtime/lua/vim/keymap.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								runtime/lua/vim/keymap.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | |||||||
|  | local keymap = {} | ||||||
|  |  | ||||||
|  | --- Add a new |mapping|. | ||||||
|  | --- Examples: | ||||||
|  | --- <pre> | ||||||
|  | ---   -- Can add mapping to Lua functions | ||||||
|  | ---   vim.keymap.set('n', 'lhs', function() print("real lua function") end) | ||||||
|  | --- | ||||||
|  | ---   -- Can use it to map multiple modes | ||||||
|  | ---   vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer=true }) | ||||||
|  | --- | ||||||
|  | ---   -- Can add mapping for specific buffer | ||||||
|  | ---   vim.keymap.set('n', '<leader>w', "<cmd>w<cr>", { silent = true, buffer = 5 }) | ||||||
|  | --- | ||||||
|  | ---   -- Expr mappings | ||||||
|  | ---   vim.keymap.set('i', '<Tab>', function() | ||||||
|  | ---     return vim.fn.pumvisible() == 1 and "<C-n>" or "<Tab>" | ||||||
|  | ---   end, { expr = true }) | ||||||
|  | ---   -- <Plug> mappings | ||||||
|  | ---   vim.keymap.set('n', '[%%', '<Plug>(MatchitNormalMultiBackward)') | ||||||
|  | --- </pre> | ||||||
|  | --- | ||||||
|  | --- Note that in a mapping like: | ||||||
|  | --- <pre> | ||||||
|  | ---    vim.keymap.set('n', 'asdf', require('jkl').my_fun) | ||||||
|  | --- </pre> | ||||||
|  | --- | ||||||
|  | --- the require('jkl') gets evaluated during this call in order to access the function. If you want to | ||||||
|  | --- avoid this cost at startup you can wrap it in a function, for example: | ||||||
|  | --- <pre> | ||||||
|  | ---    vim.keymap.set('n', 'asdf', function() return require('jkl').my_fun() end) | ||||||
|  | --- </pre> | ||||||
|  | --- | ||||||
|  | ---@param mode string|table   Same mode short names as |nvim_set_keymap()|. | ||||||
|  | ---                            Can also be list of modes to create mapping on multiple modes. | ||||||
|  | ---@param lhs string          Left-hand side |{lhs}| of the mapping. | ||||||
|  | ---@param rhs string|function  Right-hand side |{rhs}| of the mapping. Can also be a Lua function. | ||||||
|  | -- | ||||||
|  | ---@param opts table A table of |:map-arguments| such as "silent". In addition to the options | ||||||
|  | ---                  listed in |nvim_set_keymap()|, this table also accepts the following keys: | ||||||
|  | ---                  - replace_keycodes: (boolean, default true) When both this and expr is "true", | ||||||
|  | ---                  |nvim_replace_termcodes()| is applied to the result of Lua expr maps. | ||||||
|  | ---                  - remap: (boolean) Make the mapping recursive. This is the | ||||||
|  | ---                  inverse of the "noremap" option from |nvim_set_keymap()|. | ||||||
|  | ---                  Default `true` if `lhs` is a string starting with `<plug>` (case-insensitive), `false` otherwise. | ||||||
|  | ---@see |nvim_set_keymap()| | ||||||
|  | function keymap.set(mode, lhs, rhs, opts) | ||||||
|  |   vim.validate { | ||||||
|  |     mode = {mode, {'s', 't'}}, | ||||||
|  |     lhs = {lhs, 's'}, | ||||||
|  |     rhs = {rhs, {'s', 'f'}}, | ||||||
|  |     opts = {opts, 't', true} | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   opts = vim.deepcopy(opts) or {} | ||||||
|  |   local is_rhs_luaref = type(rhs) == "function" | ||||||
|  |   mode = type(mode) == 'string' and {mode} or mode | ||||||
|  |  | ||||||
|  |   if is_rhs_luaref and opts.expr and opts.replace_keycodes ~= false then | ||||||
|  |     local user_rhs = rhs | ||||||
|  |     rhs = function () | ||||||
|  |       return vim.api.nvim_replace_termcodes(user_rhs(), true, true, true) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |   -- clear replace_keycodes from opts table | ||||||
|  |   opts.replace_keycodes = nil | ||||||
|  |  | ||||||
|  |   if opts.remap == nil then | ||||||
|  |     -- remap by default on <plug> mappings and don't otherwise. | ||||||
|  |     opts.noremap = is_rhs_luaref or rhs:lower():match("^<plug>") == nil | ||||||
|  |   else | ||||||
|  |     -- remaps behavior is opposite of noremap option. | ||||||
|  |     opts.noremap = not opts.remap | ||||||
|  |     opts.remap = nil | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   if is_rhs_luaref then | ||||||
|  |     opts.callback = rhs | ||||||
|  |     rhs = '' | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   if opts.buffer then | ||||||
|  |     local bufnr = opts.buffer == true and 0 or opts.buffer | ||||||
|  |     opts.buffer = nil | ||||||
|  |     for _, m in ipairs(mode) do | ||||||
|  |       vim.api.nvim_buf_set_keymap(bufnr, m, lhs, rhs, opts) | ||||||
|  |     end | ||||||
|  |   else | ||||||
|  |     opts.buffer = nil | ||||||
|  |     for _, m in ipairs(mode) do | ||||||
|  |       vim.api.nvim_set_keymap(m, lhs, rhs, opts) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | --- Remove an existing mapping. | ||||||
|  | --- Examples: | ||||||
|  | --- <pre> | ||||||
|  | ---   vim.keymap.del('n', 'lhs') | ||||||
|  | --- | ||||||
|  | ---   vim.keymap.del({'n', 'i', 'v'}, '<leader>w', { buffer = 5 }) | ||||||
|  | --- </pre> | ||||||
|  | ---@param opts table A table of optional arguments: | ||||||
|  | ---                  - buffer: (number or boolean) Remove a mapping from the given buffer. | ||||||
|  | ---                  When "true" or 0, use the current buffer. | ||||||
|  | ---@see |vim.keymap.set()| | ||||||
|  | --- | ||||||
|  | function keymap.del(modes, lhs, opts) | ||||||
|  |   vim.validate { | ||||||
|  |     mode = {modes, {'s', 't'}}, | ||||||
|  |     lhs = {lhs, 's'}, | ||||||
|  |     opts = {opts, 't', true} | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   opts = opts or {} | ||||||
|  |   modes = type(modes) == 'string' and {modes} or modes | ||||||
|  |  | ||||||
|  |   local buffer = false | ||||||
|  |   if opts.buffer ~= nil then | ||||||
|  |     buffer = opts.buffer == true and 0 or opts.buffer | ||||||
|  |     opts.buffer = nil | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   if buffer == false then | ||||||
|  |     for _, mode in ipairs(modes) do | ||||||
|  |       vim.api.nvim_del_keymap(mode, lhs) | ||||||
|  |     end | ||||||
|  |   else | ||||||
|  |     for _, mode in ipairs(modes) do | ||||||
|  |       vim.api.nvim_buf_del_keymap(buffer, mode, lhs) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | return keymap | ||||||
| @@ -129,6 +129,7 @@ CONFIG = { | |||||||
|             'uri.lua', |             'uri.lua', | ||||||
|             'ui.lua', |             'ui.lua', | ||||||
|             'filetype.lua', |             'filetype.lua', | ||||||
|  |             'keymap.lua', | ||||||
|         ], |         ], | ||||||
|         'files': ' '.join([ |         'files': ' '.join([ | ||||||
|             os.path.join(base_dir, 'src/nvim/lua/vim.lua'), |             os.path.join(base_dir, 'src/nvim/lua/vim.lua'), | ||||||
| @@ -136,6 +137,7 @@ CONFIG = { | |||||||
|             os.path.join(base_dir, 'runtime/lua/vim/uri.lua'), |             os.path.join(base_dir, 'runtime/lua/vim/uri.lua'), | ||||||
|             os.path.join(base_dir, 'runtime/lua/vim/ui.lua'), |             os.path.join(base_dir, 'runtime/lua/vim/ui.lua'), | ||||||
|             os.path.join(base_dir, 'runtime/lua/vim/filetype.lua'), |             os.path.join(base_dir, 'runtime/lua/vim/filetype.lua'), | ||||||
|  |             os.path.join(base_dir, 'runtime/lua/vim/keymap.lua'), | ||||||
|         ]), |         ]), | ||||||
|         'file_patterns': '*.lua', |         'file_patterns': '*.lua', | ||||||
|         'fn_name_prefix': '', |         'fn_name_prefix': '', | ||||||
| @@ -151,6 +153,7 @@ CONFIG = { | |||||||
|             'uri': 'vim', |             'uri': 'vim', | ||||||
|             'ui': 'vim.ui', |             'ui': 'vim.ui', | ||||||
|             'filetype': 'vim.filetype', |             'filetype': 'vim.filetype', | ||||||
|  |             'keymap': 'vim.keymap', | ||||||
|         }, |         }, | ||||||
|         'append_only': [ |         'append_only': [ | ||||||
|             'shared.lua', |             'shared.lua', | ||||||
|   | |||||||
| @@ -115,6 +115,9 @@ setmetatable(vim, { | |||||||
|     elseif key == 'ui' then |     elseif key == 'ui' then | ||||||
|       t.ui = require('vim.ui') |       t.ui = require('vim.ui') | ||||||
|       return t.ui |       return t.ui | ||||||
|  |     elseif key == 'keymap' then | ||||||
|  |       t.keymap = require('vim.keymap') | ||||||
|  |       return t.keymap | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| }) | }) | ||||||
|   | |||||||
| @@ -2298,3 +2298,82 @@ describe('lua: require("mod") from packages', function() | |||||||
|     eq('I am fancy_z.lua', exec_lua [[ return require'fancy_z' ]]) |     eq('I am fancy_z.lua', exec_lua [[ return require'fancy_z' ]]) | ||||||
|   end) |   end) | ||||||
| end) | end) | ||||||
|  |  | ||||||
|  | describe('vim.keymap', function() | ||||||
|  |   it('can make a mapping', function() | ||||||
|  |     eq(0, exec_lua [[ | ||||||
|  |       GlobalCount = 0 | ||||||
|  |       vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end) | ||||||
|  |       return GlobalCount | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |     feed('asdf\n') | ||||||
|  |  | ||||||
|  |     eq(1, exec_lua[[return GlobalCount]]) | ||||||
|  |   end) | ||||||
|  |  | ||||||
|  |   it('can make an expr mapping', function() | ||||||
|  |     exec_lua [[ | ||||||
|  |       vim.keymap.set('n', 'aa', function() return ':lua SomeValue = 99<cr>' end, {expr = true}) | ||||||
|  |     ]] | ||||||
|  |  | ||||||
|  |     feed('aa') | ||||||
|  |  | ||||||
|  |     eq(99, exec_lua[[return SomeValue]]) | ||||||
|  |   end) | ||||||
|  |  | ||||||
|  |   it('can overwrite a mapping', function() | ||||||
|  |     eq(0, exec_lua [[ | ||||||
|  |       GlobalCount = 0 | ||||||
|  |       vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end) | ||||||
|  |       return GlobalCount | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |     feed('asdf\n') | ||||||
|  |  | ||||||
|  |     eq(1, exec_lua[[return GlobalCount]]) | ||||||
|  |  | ||||||
|  |     exec_lua [[ | ||||||
|  |       vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount - 1 end) | ||||||
|  |     ]] | ||||||
|  |  | ||||||
|  |     feed('asdf\n') | ||||||
|  |  | ||||||
|  |     eq(0, exec_lua[[return GlobalCount]]) | ||||||
|  |   end) | ||||||
|  |  | ||||||
|  |   it('can unmap a mapping', function() | ||||||
|  |     eq(0, exec_lua [[ | ||||||
|  |       GlobalCount = 0 | ||||||
|  |       vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end) | ||||||
|  |       return GlobalCount | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |     feed('asdf\n') | ||||||
|  |  | ||||||
|  |     eq(1, exec_lua[[return GlobalCount]]) | ||||||
|  |  | ||||||
|  |     exec_lua [[ | ||||||
|  |       vim.keymap.del('n', 'asdf') | ||||||
|  |     ]] | ||||||
|  |  | ||||||
|  |     feed('asdf\n') | ||||||
|  |  | ||||||
|  |     eq(1, exec_lua[[return GlobalCount]]) | ||||||
|  |     eq('\nNo mapping found', helpers.exec_capture('nmap asdf')) | ||||||
|  |   end) | ||||||
|  |  | ||||||
|  |   it('can do <Plug> mappings', function() | ||||||
|  |     eq(0, exec_lua [[ | ||||||
|  |       GlobalCount = 0 | ||||||
|  |       vim.keymap.set('n', '<plug>(asdf)', function() GlobalCount = GlobalCount + 1 end) | ||||||
|  |       vim.keymap.set('n', 'ww', '<plug>(asdf)') | ||||||
|  |       return GlobalCount | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |     feed('ww\n') | ||||||
|  |  | ||||||
|  |     eq(1, exec_lua[[return GlobalCount]]) | ||||||
|  |   end) | ||||||
|  |  | ||||||
|  | end) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 shadmansaleh
					shadmansaleh