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 | ||||
|                                  (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: | ||||
|   | ||||
							
								
								
									
										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', | ||||
|             'ui.lua', | ||||
|             'filetype.lua', | ||||
|             'keymap.lua', | ||||
|         ], | ||||
|         'files': ' '.join([ | ||||
|             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/ui.lua'), | ||||
|             os.path.join(base_dir, 'runtime/lua/vim/filetype.lua'), | ||||
|             os.path.join(base_dir, 'runtime/lua/vim/keymap.lua'), | ||||
|         ]), | ||||
|         'file_patterns': '*.lua', | ||||
|         'fn_name_prefix': '', | ||||
| @@ -151,6 +153,7 @@ CONFIG = { | ||||
|             'uri': 'vim', | ||||
|             'ui': 'vim.ui', | ||||
|             'filetype': 'vim.filetype', | ||||
|             'keymap': 'vim.keymap', | ||||
|         }, | ||||
|         'append_only': [ | ||||
|             'shared.lua', | ||||
|   | ||||
| @@ -115,6 +115,9 @@ setmetatable(vim, { | ||||
|     elseif key == 'ui' then | ||||
|       t.ui = require('vim.ui') | ||||
|       return t.ui | ||||
|     elseif key == 'keymap' then | ||||
|       t.keymap = require('vim.keymap') | ||||
|       return t.keymap | ||||
|     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' ]]) | ||||
|   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