keymap_spec.lua 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169
  1. local helpers = require('test.functional.helpers')(after_each)
  2. local bufmeths = helpers.bufmeths
  3. local clear = helpers.clear
  4. local command = helpers.command
  5. local curbufmeths = helpers.curbufmeths
  6. local eq, neq = helpers.eq, helpers.neq
  7. local exec_lua = helpers.exec_lua
  8. local exec = helpers.exec
  9. local feed = helpers.feed
  10. local funcs = helpers.funcs
  11. local meths = helpers.meths
  12. local source = helpers.source
  13. local pcall_err = helpers.pcall_err
  14. local shallowcopy = helpers.shallowcopy
  15. local sleep = helpers.sleep
  16. local sid_api_client = -9
  17. local sid_lua = -8
  18. describe('nvim_get_keymap', function()
  19. before_each(clear)
  20. -- Basic mapping and table to be used to describe results
  21. local foo_bar_string = 'nnoremap foo bar'
  22. local foo_bar_map_table = {
  23. lhs='foo',
  24. lhsraw='foo',
  25. script=0,
  26. silent=0,
  27. rhs='bar',
  28. expr=0,
  29. sid=0,
  30. buffer=0,
  31. nowait=0,
  32. mode='n',
  33. noremap=1,
  34. lnum=0,
  35. }
  36. it('returns empty list when no map', function()
  37. eq({}, meths.get_keymap('n'))
  38. end)
  39. it('returns list of all applicable mappings', function()
  40. command(foo_bar_string)
  41. -- Only one mapping available
  42. -- Should be the same as the dictionary we supplied earlier
  43. -- and the dictionary you would get from maparg
  44. -- since this is a global map, and not script local
  45. eq({foo_bar_map_table}, meths.get_keymap('n'))
  46. eq({funcs.maparg('foo', 'n', false, true)},
  47. meths.get_keymap('n')
  48. )
  49. -- Add another mapping
  50. command('nnoremap foo_longer bar_longer')
  51. local foolong_bar_map_table = shallowcopy(foo_bar_map_table)
  52. foolong_bar_map_table['lhs'] = 'foo_longer'
  53. foolong_bar_map_table['lhsraw'] = 'foo_longer'
  54. foolong_bar_map_table['rhs'] = 'bar_longer'
  55. eq({foolong_bar_map_table, foo_bar_map_table},
  56. meths.get_keymap('n')
  57. )
  58. -- Remove a mapping
  59. command('unmap foo_longer')
  60. eq({foo_bar_map_table},
  61. meths.get_keymap('n')
  62. )
  63. end)
  64. it('works for other modes', function()
  65. -- Add two mappings, one in insert and one normal
  66. -- We'll only check the insert mode one
  67. command('nnoremap not_going to_check')
  68. command('inoremap foo bar')
  69. -- The table will be the same except for the mode
  70. local insert_table = shallowcopy(foo_bar_map_table)
  71. insert_table['mode'] = 'i'
  72. eq({insert_table}, meths.get_keymap('i'))
  73. end)
  74. it('considers scope', function()
  75. -- change the map slightly
  76. command('nnoremap foo_longer bar_longer')
  77. local foolong_bar_map_table = shallowcopy(foo_bar_map_table)
  78. foolong_bar_map_table['lhs'] = 'foo_longer'
  79. foolong_bar_map_table['lhsraw'] = 'foo_longer'
  80. foolong_bar_map_table['rhs'] = 'bar_longer'
  81. local buffer_table = shallowcopy(foo_bar_map_table)
  82. buffer_table['buffer'] = 1
  83. command('nnoremap <buffer> foo bar')
  84. -- The buffer mapping should not show up
  85. eq({foolong_bar_map_table}, meths.get_keymap('n'))
  86. eq({buffer_table}, curbufmeths.get_keymap('n'))
  87. end)
  88. it('considers scope for overlapping maps', function()
  89. command('nnoremap foo bar')
  90. local buffer_table = shallowcopy(foo_bar_map_table)
  91. buffer_table['buffer'] = 1
  92. command('nnoremap <buffer> foo bar')
  93. eq({foo_bar_map_table}, meths.get_keymap('n'))
  94. eq({buffer_table}, curbufmeths.get_keymap('n'))
  95. end)
  96. it('can retrieve mapping for different buffers', function()
  97. local original_buffer = curbufmeths.get_number()
  98. -- Place something in each of the buffers to make sure they stick around
  99. -- and set hidden so we can leave them
  100. command('set hidden')
  101. command('new')
  102. command('normal! ihello 2')
  103. command('new')
  104. command('normal! ihello 3')
  105. local final_buffer = curbufmeths.get_number()
  106. command('nnoremap <buffer> foo bar')
  107. -- Final buffer will have buffer mappings
  108. local buffer_table = shallowcopy(foo_bar_map_table)
  109. buffer_table['buffer'] = final_buffer
  110. eq({buffer_table}, meths.buf_get_keymap(final_buffer, 'n'))
  111. eq({buffer_table}, meths.buf_get_keymap(0, 'n'))
  112. command('buffer ' .. original_buffer)
  113. eq(original_buffer, curbufmeths.get_number())
  114. -- Original buffer won't have any mappings
  115. eq({}, meths.get_keymap('n'))
  116. eq({}, curbufmeths.get_keymap('n'))
  117. eq({buffer_table}, meths.buf_get_keymap(final_buffer, 'n'))
  118. end)
  119. -- Test toggle switches for basic options
  120. -- @param option The key represented in the `maparg()` result dict
  121. local function global_and_buffer_test(map,
  122. option,
  123. option_token,
  124. global_on_result,
  125. buffer_on_result,
  126. global_off_result,
  127. buffer_off_result,
  128. new_windows)
  129. local function make_new_windows(number_of_windows)
  130. if new_windows == nil then
  131. return nil
  132. end
  133. for _=1,number_of_windows do
  134. command('new')
  135. end
  136. end
  137. local mode = string.sub(map, 1,1)
  138. -- Don't run this for the <buffer> mapping, since it doesn't make sense
  139. if option_token ~= '<buffer>' then
  140. it(string.format( 'returns %d for the key "%s" when %s is used globally with %s (%s)',
  141. global_on_result, option, option_token, map, mode), function()
  142. make_new_windows(new_windows)
  143. command(map .. ' ' .. option_token .. ' foo bar')
  144. local result = meths.get_keymap(mode)[1][option]
  145. eq(global_on_result, result)
  146. end)
  147. end
  148. it(string.format('returns %d for the key "%s" when %s is used for buffers with %s (%s)',
  149. buffer_on_result, option, option_token, map, mode), function()
  150. make_new_windows(new_windows)
  151. command(map .. ' <buffer> ' .. option_token .. ' foo bar')
  152. local result = curbufmeths.get_keymap(mode)[1][option]
  153. eq(buffer_on_result, result)
  154. end)
  155. -- Don't run these for the <buffer> mapping, since it doesn't make sense
  156. if option_token ~= '<buffer>' then
  157. it(string.format('returns %d for the key "%s" when %s is not used globally with %s (%s)',
  158. global_off_result, option, option_token, map, mode), function()
  159. make_new_windows(new_windows)
  160. command(map .. ' baz bat')
  161. local result = meths.get_keymap(mode)[1][option]
  162. eq(global_off_result, result)
  163. end)
  164. it(string.format('returns %d for the key "%s" when %s is not used for buffers with %s (%s)',
  165. buffer_off_result, option, option_token, map, mode), function()
  166. make_new_windows(new_windows)
  167. command(map .. ' <buffer> foo bar')
  168. local result = curbufmeths.get_keymap(mode)[1][option]
  169. eq(buffer_off_result, result)
  170. end)
  171. end
  172. end
  173. -- Standard modes and returns the same values in the dictionary as maparg()
  174. local mode_list = {'nnoremap', 'nmap', 'imap', 'inoremap', 'cnoremap'}
  175. for mode in pairs(mode_list) do
  176. global_and_buffer_test(mode_list[mode], 'silent', '<silent>', 1, 1, 0, 0)
  177. global_and_buffer_test(mode_list[mode], 'nowait', '<nowait>', 1, 1, 0, 0)
  178. global_and_buffer_test(mode_list[mode], 'expr', '<expr>', 1, 1, 0, 0)
  179. end
  180. -- noremap will now be 2 if script was used, which is not the same as maparg()
  181. global_and_buffer_test('nmap', 'noremap', '<script>', 2, 2, 0, 0)
  182. global_and_buffer_test('nnoremap', 'noremap', '<script>', 2, 2, 1, 1)
  183. -- buffer will return the buffer ID, which is not the same as maparg()
  184. -- Three of these tests won't run
  185. global_and_buffer_test('nnoremap', 'buffer', '<buffer>', nil, 3, nil, nil, 2)
  186. it('returns script numbers for global maps', function()
  187. source([[
  188. function! s:maparg_test_function() abort
  189. return 'testing'
  190. endfunction
  191. nnoremap fizz :call <SID>maparg_test_function()<CR>
  192. ]])
  193. local sid_result = meths.get_keymap('n')[1]['sid']
  194. eq(1, sid_result)
  195. eq('testing', meths.call_function('<SNR>' .. sid_result .. '_maparg_test_function', {}))
  196. end)
  197. it('returns script numbers for buffer maps', function()
  198. source([[
  199. function! s:maparg_test_function() abort
  200. return 'testing'
  201. endfunction
  202. nnoremap <buffer> fizz :call <SID>maparg_test_function()<CR>
  203. ]])
  204. local sid_result = curbufmeths.get_keymap('n')[1]['sid']
  205. eq(1, sid_result)
  206. eq('testing', meths.call_function('<SNR>' .. sid_result .. '_maparg_test_function', {}))
  207. end)
  208. it('works with <F12> and others', function()
  209. command('nnoremap <F12> :let g:maparg_test_var = 1<CR>')
  210. eq('<F12>', meths.get_keymap('n')[1]['lhs'])
  211. eq(':let g:maparg_test_var = 1<CR>', meths.get_keymap('n')[1]['rhs'])
  212. end)
  213. it('works correctly despite various &cpo settings', function()
  214. local cpo_table = {
  215. script=0,
  216. silent=0,
  217. expr=0,
  218. sid=0,
  219. buffer=0,
  220. nowait=0,
  221. noremap=1,
  222. lnum=0,
  223. }
  224. local function cpomap(lhs, rhs, mode)
  225. local ret = shallowcopy(cpo_table)
  226. ret.lhs = lhs
  227. ret.rhs = rhs
  228. ret.mode = mode
  229. return ret
  230. end
  231. command('set cpo+=B')
  232. command('nnoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\')
  233. command('nnoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\')
  234. command('set cpo+=B')
  235. command('xnoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\')
  236. command('xnoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\')
  237. command('set cpo-=B')
  238. command('snoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\')
  239. command('snoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\')
  240. command('set cpo-=B')
  241. command('onoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\')
  242. command('onoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\')
  243. -- wrapper around get_keymap() that drops "lhsraw" and "lhsrawalt" which are hard to check
  244. local function get_keymap_noraw(...)
  245. local ret = meths.get_keymap(...)
  246. for _, item in ipairs(ret) do
  247. item.lhsraw = nil
  248. item.lhsrawalt = nil
  249. end
  250. return ret
  251. end
  252. for _, cmd in ipairs({
  253. 'set cpo-=B',
  254. 'set cpo+=B',
  255. }) do
  256. command(cmd)
  257. eq({cpomap('\\<C-C><C-C><lt>C-c>\\', '\\<C-D><C-D><lt>C-d>\\', 'n'),
  258. cpomap('\\<C-A><C-A><lt>C-a>\\', '\\<C-B><C-B><lt>C-b>\\', 'n')},
  259. get_keymap_noraw('n'))
  260. eq({cpomap('\\<C-C><C-C><lt>C-c>\\', '\\<C-D><C-D><lt>C-d>\\', 'x'),
  261. cpomap('\\<C-A><C-A><lt>C-a>\\', '\\<C-B><C-B><lt>C-b>\\', 'x')},
  262. get_keymap_noraw('x'))
  263. eq({cpomap('<lt>C-c><C-C><lt>C-c> ', '<lt>C-d><C-D><lt>C-d>', 's'),
  264. cpomap('<lt>C-a><C-A><lt>C-a> ', '<lt>C-b><C-B><lt>C-b>', 's')},
  265. get_keymap_noraw('s'))
  266. eq({cpomap('<lt>C-c><C-C><lt>C-c> ', '<lt>C-d><C-D><lt>C-d>', 'o'),
  267. cpomap('<lt>C-a><C-A><lt>C-a> ', '<lt>C-b><C-B><lt>C-b>', 'o')},
  268. get_keymap_noraw('o'))
  269. end
  270. end)
  271. it('always uses space for space and bar for bar', function()
  272. local space_table = {
  273. lhs='| |',
  274. lhsraw='| |',
  275. rhs='| |',
  276. mode='n',
  277. script=0,
  278. silent=0,
  279. expr=0,
  280. sid=0,
  281. buffer=0,
  282. nowait=0,
  283. noremap=1,
  284. lnum=0,
  285. }
  286. command('nnoremap \\|<Char-0x20><Char-32><Space><Bar> \\|<Char-0x20><Char-32><Space> <Bar>')
  287. eq({space_table}, meths.get_keymap('n'))
  288. end)
  289. it('can handle lua mappings', function()
  290. eq(0, exec_lua([[
  291. GlobalCount = 0
  292. vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
  293. return GlobalCount
  294. ]]))
  295. feed('asdf\n')
  296. eq(1, exec_lua([[return GlobalCount]]))
  297. eq(2, exec_lua([[
  298. vim.api.nvim_get_keymap('n')[1].callback()
  299. return GlobalCount
  300. ]]))
  301. exec([[
  302. call nvim_get_keymap('n')[0].callback()
  303. ]])
  304. eq(3, exec_lua([[return GlobalCount]]))
  305. local mapargs = meths.get_keymap('n')
  306. mapargs[1].callback = nil
  307. eq({
  308. lhs='asdf',
  309. lhsraw='asdf',
  310. script=0,
  311. silent=0,
  312. expr=0,
  313. sid=sid_lua,
  314. buffer=0,
  315. nowait=0,
  316. mode='n',
  317. noremap=0,
  318. lnum=0,
  319. }, mapargs[1])
  320. end)
  321. it ('can handle map descriptions', function()
  322. meths.set_keymap('n', 'lhs', 'rhs', {desc="map description"})
  323. eq({
  324. lhs='lhs',
  325. lhsraw='lhs',
  326. rhs='rhs',
  327. script=0,
  328. silent=0,
  329. expr=0,
  330. sid=sid_api_client,
  331. buffer=0,
  332. nowait=0,
  333. mode='n',
  334. noremap=0,
  335. lnum=0,
  336. desc='map description'
  337. }, meths.get_keymap('n')[1])
  338. end)
  339. end)
  340. describe('nvim_set_keymap, nvim_del_keymap', function()
  341. before_each(clear)
  342. -- `generate_expected` is truthy: for generating an expected output for
  343. -- maparg(), which does not accept "!" (though it returns "!" in its output
  344. -- if getting a mapping set with |:map!|).
  345. local function normalize_mapmode(mode, generate_expected)
  346. if not generate_expected and mode == '!' then
  347. -- Cannot retrieve mapmode-ic mappings with "!", but can with "i" or "c".
  348. mode = 'i'
  349. elseif mode == '' then
  350. mode = generate_expected and ' ' or mode
  351. end
  352. return mode
  353. end
  354. -- Generate a mapargs dict, for comparison against the mapping that was
  355. -- actually set
  356. local function generate_mapargs(mode, lhs, rhs, opts)
  357. if not opts then
  358. opts = {}
  359. end
  360. local to_return = {}
  361. to_return.mode = normalize_mapmode(mode, true)
  362. to_return.noremap = not opts.noremap and 0 or 1
  363. to_return.lhs = lhs
  364. to_return.rhs = rhs
  365. to_return.script = 0
  366. to_return.silent = not opts.silent and 0 or 1
  367. to_return.nowait = not opts.nowait and 0 or 1
  368. to_return.expr = not opts.expr and 0 or 1
  369. to_return.sid = not opts.sid and sid_api_client or opts.sid
  370. to_return.buffer = not opts.buffer and 0 or opts.buffer
  371. to_return.lnum = not opts.lnum and 0 or opts.lnum
  372. to_return.desc = opts.desc
  373. return to_return
  374. end
  375. -- Gets a maparg() dict from Nvim, if one exists.
  376. local function get_mapargs(mode, lhs)
  377. local mapargs = funcs.maparg(lhs, normalize_mapmode(mode), false, true)
  378. -- drop "lhsraw" and "lhsrawalt" which are hard to check
  379. mapargs.lhsraw = nil
  380. mapargs.lhsrawalt = nil
  381. return mapargs
  382. end
  383. it('error on empty LHS', function()
  384. -- escape parentheses in lua string, else comparison fails erroneously
  385. eq('Invalid (empty) LHS', pcall_err(meths.set_keymap, '', '', 'rhs', {}))
  386. eq('Invalid (empty) LHS', pcall_err(meths.set_keymap, '', '', '', {}))
  387. eq('Invalid (empty) LHS', pcall_err(meths.del_keymap, '', ''))
  388. end)
  389. it('error if LHS longer than MAXMAPLEN', function()
  390. -- assume MAXMAPLEN of 50 chars, as declared in vim.h
  391. local MAXMAPLEN = 50
  392. local lhs = ''
  393. for i=1,MAXMAPLEN do
  394. lhs = lhs..(i % 10)
  395. end
  396. -- exactly 50 chars should be fine
  397. meths.set_keymap('', lhs, 'rhs', {})
  398. -- del_keymap should unmap successfully
  399. meths.del_keymap('', lhs)
  400. eq({}, get_mapargs('', lhs))
  401. -- 51 chars should produce an error
  402. lhs = lhs..'1'
  403. eq('LHS exceeds maximum map length: '..lhs,
  404. pcall_err(meths.set_keymap, '', lhs, 'rhs', {}))
  405. eq('LHS exceeds maximum map length: '..lhs,
  406. pcall_err(meths.del_keymap, '', lhs))
  407. end)
  408. it('does not throw errors when rhs is longer than MAXMAPLEN', function()
  409. local MAXMAPLEN = 50
  410. local rhs = ''
  411. for i=1,MAXMAPLEN do
  412. rhs = rhs..(i % 10)
  413. end
  414. rhs = rhs..'1'
  415. meths.set_keymap('', 'lhs', rhs, {})
  416. eq(generate_mapargs('', 'lhs', rhs),
  417. get_mapargs('', 'lhs'))
  418. end)
  419. it('throws errors when given too-long mode shortnames', function()
  420. eq('Shortname is too long: map',
  421. pcall_err(meths.set_keymap, 'map', 'lhs', 'rhs', {}))
  422. eq('Shortname is too long: vmap',
  423. pcall_err(meths.set_keymap, 'vmap', 'lhs', 'rhs', {}))
  424. eq('Shortname is too long: xnoremap',
  425. pcall_err(meths.set_keymap, 'xnoremap', 'lhs', 'rhs', {}))
  426. eq('Shortname is too long: map', pcall_err(meths.del_keymap, 'map', 'lhs'))
  427. eq('Shortname is too long: vmap', pcall_err(meths.del_keymap, 'vmap', 'lhs'))
  428. eq('Shortname is too long: xnoremap', pcall_err(meths.del_keymap, 'xnoremap', 'lhs'))
  429. end)
  430. it('error on invalid mode shortname', function()
  431. eq('Invalid mode shortname: " "',
  432. pcall_err(meths.set_keymap, ' ', 'lhs', 'rhs', {}))
  433. eq('Invalid mode shortname: "m"',
  434. pcall_err(meths.set_keymap, 'm', 'lhs', 'rhs', {}))
  435. eq('Invalid mode shortname: "?"',
  436. pcall_err(meths.set_keymap, '?', 'lhs', 'rhs', {}))
  437. eq('Invalid mode shortname: "y"',
  438. pcall_err(meths.set_keymap, 'y', 'lhs', 'rhs', {}))
  439. eq('Invalid mode shortname: "p"',
  440. pcall_err(meths.set_keymap, 'p', 'lhs', 'rhs', {}))
  441. eq('Invalid mode shortname: "?"', pcall_err(meths.del_keymap, '?', 'lhs'))
  442. eq('Invalid mode shortname: "y"', pcall_err(meths.del_keymap, 'y', 'lhs'))
  443. eq('Invalid mode shortname: "p"', pcall_err(meths.del_keymap, 'p', 'lhs'))
  444. end)
  445. it('error on invalid optnames', function()
  446. eq("Invalid key: 'silentt'",
  447. pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {silentt = true}))
  448. eq("Invalid key: 'sidd'",
  449. pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {sidd = false}))
  450. eq("Invalid key: 'nowaiT'",
  451. pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {nowaiT = false}))
  452. end)
  453. it('error on <buffer> option key', function()
  454. eq("Invalid key: 'buffer'",
  455. pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {buffer = true}))
  456. end)
  457. it('error when "replace_keycodes" is used without "expr"', function()
  458. eq('"replace_keycodes" requires "expr"',
  459. pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {replace_keycodes = true}))
  460. end)
  461. local optnames = {'nowait', 'silent', 'script', 'expr', 'unique'}
  462. for _, opt in ipairs(optnames) do
  463. -- note: need '%' to escape hyphens, which have special meaning in lua
  464. it('throws an error when given non-boolean value for '..opt, function()
  465. local opts = {}
  466. opts[opt] = 'fooo'
  467. eq(opt..' is not a boolean',
  468. pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', opts))
  469. end)
  470. end
  471. -- Perform tests of basic functionality
  472. it('sets ordinary mappings', function()
  473. meths.set_keymap('n', 'lhs', 'rhs', {})
  474. eq(generate_mapargs('n', 'lhs', 'rhs'), get_mapargs('n', 'lhs'))
  475. meths.set_keymap('v', 'lhs', 'rhs', {})
  476. eq(generate_mapargs('v', 'lhs', 'rhs'), get_mapargs('v', 'lhs'))
  477. end)
  478. it('does not throw when LHS or RHS have leading/trailing whitespace', function()
  479. meths.set_keymap('n', ' lhs', 'rhs', {})
  480. eq(generate_mapargs('n', '<Space><Space><Space>lhs', 'rhs'),
  481. get_mapargs('n', ' lhs'))
  482. meths.set_keymap('n', 'lhs ', 'rhs', {})
  483. eq(generate_mapargs('n', 'lhs<Space><Space><Space><Space>', 'rhs'),
  484. get_mapargs('n', 'lhs '))
  485. meths.set_keymap('v', ' lhs ', '\trhs\t\f', {})
  486. eq(generate_mapargs('v', '<Space>lhs<Space><Space>', '\trhs\t\f'),
  487. get_mapargs('v', ' lhs '))
  488. end)
  489. it('can set noremap mappings', function()
  490. meths.set_keymap('x', 'lhs', 'rhs', {noremap = true})
  491. eq(generate_mapargs('x', 'lhs', 'rhs', {noremap = true}),
  492. get_mapargs('x', 'lhs'))
  493. meths.set_keymap('t', 'lhs', 'rhs', {noremap = true})
  494. eq(generate_mapargs('t', 'lhs', 'rhs', {noremap = true}),
  495. get_mapargs('t', 'lhs'))
  496. end)
  497. it('can unmap mappings', function()
  498. meths.set_keymap('v', 'lhs', 'rhs', {})
  499. meths.del_keymap('v', 'lhs')
  500. eq({}, get_mapargs('v', 'lhs'))
  501. meths.set_keymap('t', 'lhs', 'rhs', {noremap = true})
  502. meths.del_keymap('t', 'lhs')
  503. eq({}, get_mapargs('t', 'lhs'))
  504. end)
  505. -- Test some edge cases
  506. it('"!" and empty string are synonyms for mapmode-nvo', function()
  507. local nvo_shortnames = {'', '!'}
  508. for _, name in ipairs(nvo_shortnames) do
  509. meths.set_keymap(name, 'lhs', 'rhs', {})
  510. meths.del_keymap(name, 'lhs')
  511. eq({}, get_mapargs(name, 'lhs'))
  512. end
  513. end)
  514. local special_chars = {'<C-U>', '<S-Left>', '<F12><F2><Tab>', '<Space><Tab>'}
  515. for _, lhs in ipairs(special_chars) do
  516. for _, rhs in ipairs(special_chars) do
  517. local mapmode = '!'
  518. it('can set mappings with special characters, lhs: '..lhs..', rhs: '..rhs,
  519. function()
  520. meths.set_keymap(mapmode, lhs, rhs, {})
  521. eq(generate_mapargs(mapmode, lhs, rhs), get_mapargs(mapmode, lhs))
  522. end)
  523. end
  524. end
  525. it('can set mappings containing literal keycodes', function()
  526. meths.set_keymap('n', '\n\r\n', 'rhs', {})
  527. local expected = generate_mapargs('n', '<NL><CR><NL>', 'rhs')
  528. eq(expected, get_mapargs('n', '<NL><CR><NL>'))
  529. end)
  530. it('can set mappings whose RHS is a <Nop>', function()
  531. meths.set_keymap('i', 'lhs', '<Nop>', {})
  532. command('normal ilhs')
  533. eq({''}, curbufmeths.get_lines(0, -1, 0)) -- imap to <Nop> does nothing
  534. eq(generate_mapargs('i', 'lhs', '<Nop>', {}),
  535. get_mapargs('i', 'lhs'))
  536. -- also test for case insensitivity
  537. meths.set_keymap('i', 'lhs', '<nOp>', {})
  538. command('normal ilhs')
  539. eq({''}, curbufmeths.get_lines(0, -1, 0))
  540. -- note: RHS in returned mapargs() dict reflects the original RHS
  541. -- provided by the user
  542. eq(generate_mapargs('i', 'lhs', '<nOp>', {}),
  543. get_mapargs('i', 'lhs'))
  544. meths.set_keymap('i', 'lhs', '<NOP>', {})
  545. command('normal ilhs')
  546. eq({''}, curbufmeths.get_lines(0, -1, 0))
  547. eq(generate_mapargs('i', 'lhs', '<NOP>', {}),
  548. get_mapargs('i', 'lhs'))
  549. -- a single ^V in RHS is also <Nop> (see :h map-empty-rhs)
  550. meths.set_keymap('i', 'lhs', '\022', {})
  551. command('normal ilhs')
  552. eq({''}, curbufmeths.get_lines(0, -1, 0))
  553. eq(generate_mapargs('i', 'lhs', '\022', {}),
  554. get_mapargs('i', 'lhs'))
  555. end)
  556. it('treats an empty RHS in a mapping like a <Nop>', function()
  557. meths.set_keymap('i', 'lhs', '', {})
  558. command('normal ilhs')
  559. eq({''}, curbufmeths.get_lines(0, -1, 0))
  560. eq(generate_mapargs('i', 'lhs', '', {}),
  561. get_mapargs('i', 'lhs'))
  562. end)
  563. it('can set and unset <M-">', function()
  564. -- Taken from the legacy test: test_mapping.vim. Exposes a bug in which
  565. -- replace_termcodes changes the length of the mapping's LHS, but
  566. -- do_map continues to use the *old* length of LHS.
  567. meths.set_keymap('i', '<M-">', 'foo', {})
  568. meths.del_keymap('i', '<M-">')
  569. eq({}, get_mapargs('i', '<M-">'))
  570. end)
  571. it('interprets control sequences in expr-quotes correctly when called '
  572. ..'inside vim', function()
  573. command([[call nvim_set_keymap('i', "\<space>", "\<tab>", {})]])
  574. eq(generate_mapargs('i', '<Space>', '\t', {sid=0}),
  575. get_mapargs('i', '<Space>'))
  576. feed('i ')
  577. eq({'\t'}, curbufmeths.get_lines(0, -1, 0))
  578. end)
  579. it('throws appropriate error messages when setting <unique> maps', function()
  580. meths.set_keymap('l', 'lhs', 'rhs', {})
  581. eq('E227: mapping already exists for lhs',
  582. pcall_err(meths.set_keymap, 'l', 'lhs', 'rhs', {unique = true}))
  583. -- different mapmode, no error should be thrown
  584. meths.set_keymap('t', 'lhs', 'rhs', {unique = true})
  585. end)
  586. it('can set <expr> mappings whose RHS change dynamically', function()
  587. meths.exec([[
  588. function! FlipFlop() abort
  589. if !exists('g:flip') | let g:flip = 0 | endif
  590. let g:flip = !g:flip
  591. return g:flip
  592. endfunction
  593. ]], true)
  594. eq(1, meths.call_function('FlipFlop', {}))
  595. eq(0, meths.call_function('FlipFlop', {}))
  596. eq(1, meths.call_function('FlipFlop', {}))
  597. eq(0, meths.call_function('FlipFlop', {}))
  598. meths.set_keymap('i', 'lhs', 'FlipFlop()', {expr = true})
  599. command('normal ilhs')
  600. eq({'1'}, curbufmeths.get_lines(0, -1, 0))
  601. command('normal! ggVGd')
  602. command('normal ilhs')
  603. eq({'0'}, curbufmeths.get_lines(0, -1, 0))
  604. end)
  605. it('can set mappings that do trigger other mappings', function()
  606. meths.set_keymap('i', 'mhs', 'rhs', {})
  607. meths.set_keymap('i', 'lhs', 'mhs', {})
  608. command('normal imhs')
  609. eq({'rhs'}, curbufmeths.get_lines(0, -1, 0))
  610. command('normal! ggVGd')
  611. command('normal ilhs')
  612. eq({'rhs'}, curbufmeths.get_lines(0, -1, 0))
  613. end)
  614. it("can set noremap mappings that don't trigger other mappings", function()
  615. meths.set_keymap('i', 'mhs', 'rhs', {})
  616. meths.set_keymap('i', 'lhs', 'mhs', {noremap = true})
  617. command('normal imhs')
  618. eq({'rhs'}, curbufmeths.get_lines(0, -1, 0))
  619. command('normal! ggVGd')
  620. command('normal ilhs') -- shouldn't trigger mhs-to-rhs mapping
  621. eq({'mhs'}, curbufmeths.get_lines(0, -1, 0))
  622. end)
  623. it("can set nowait mappings that fire without waiting", function()
  624. meths.set_keymap('i', '123456', 'longer', {})
  625. meths.set_keymap('i', '123', 'shorter', {nowait = true})
  626. -- feed keys one at a time; if all keys arrive atomically, the longer
  627. -- mapping will trigger
  628. local keys = 'i123456'
  629. for c in string.gmatch(keys, '.') do
  630. feed(c)
  631. sleep(5)
  632. end
  633. eq({'shorter456'}, curbufmeths.get_lines(0, -1, 0))
  634. end)
  635. -- Perform exhaustive tests of basic functionality
  636. local mapmodes = {'n', 'v', 'x', 's', 'o', '!', 'i', 'l', 'c', 't', ''}
  637. for _, mapmode in ipairs(mapmodes) do
  638. it('can set/unset normal mappings in mapmode '..mapmode, function()
  639. meths.set_keymap(mapmode, 'lhs', 'rhs', {})
  640. eq(generate_mapargs(mapmode, 'lhs', 'rhs'),
  641. get_mapargs(mapmode, 'lhs'))
  642. -- some mapmodes (like 'o') will prevent other mapmodes (like '!') from
  643. -- taking effect, so unmap after each mapping
  644. meths.del_keymap(mapmode, 'lhs')
  645. eq({}, get_mapargs(mapmode, 'lhs'))
  646. end)
  647. end
  648. for _, mapmode in ipairs(mapmodes) do
  649. it('can set/unset noremap mappings using mapmode '..mapmode, function()
  650. meths.set_keymap(mapmode, 'lhs', 'rhs', {noremap = true})
  651. eq(generate_mapargs(mapmode, 'lhs', 'rhs', {noremap = true}),
  652. get_mapargs(mapmode, 'lhs'))
  653. meths.del_keymap(mapmode, 'lhs')
  654. eq({}, get_mapargs(mapmode, 'lhs'))
  655. end)
  656. end
  657. -- Test map-arguments, using optnames from above
  658. -- remove some map arguments that are harder to test, or were already tested
  659. optnames = {'nowait', 'silent', 'expr', 'noremap'}
  660. for _, mapmode in ipairs(mapmodes) do
  661. local printable_mode = normalize_mapmode(mapmode)
  662. -- Test with single mappings
  663. for _, maparg in ipairs(optnames) do
  664. it('can set/unset '..printable_mode..'-mappings with maparg: '..maparg,
  665. function()
  666. meths.set_keymap(mapmode, 'lhs', 'rhs', {[maparg] = true})
  667. eq(generate_mapargs(mapmode, 'lhs', 'rhs', {[maparg] = true}),
  668. get_mapargs(mapmode, 'lhs'))
  669. meths.del_keymap(mapmode, 'lhs')
  670. eq({}, get_mapargs(mapmode, 'lhs'))
  671. end)
  672. it ('can set/unset '..printable_mode..'-mode mappings with maparg '..
  673. maparg..', whose value is false', function()
  674. meths.set_keymap(mapmode, 'lhs', 'rhs', {[maparg] = false})
  675. eq(generate_mapargs(mapmode, 'lhs', 'rhs'),
  676. get_mapargs(mapmode, 'lhs'))
  677. meths.del_keymap(mapmode, 'lhs')
  678. eq({}, get_mapargs(mapmode, 'lhs'))
  679. end)
  680. end
  681. -- Test with triplets of mappings, one of which is false
  682. for i = 1, (#optnames - 2) do
  683. local opt1, opt2, opt3 = optnames[i], optnames[i + 1], optnames[i + 2]
  684. it('can set/unset '..printable_mode..'-mode mappings with mapargs '..
  685. opt1..', '..opt2..', '..opt3, function()
  686. local opts = {[opt1] = true, [opt2] = false, [opt3] = true}
  687. meths.set_keymap(mapmode, 'lhs', 'rhs', opts)
  688. eq(generate_mapargs(mapmode, 'lhs', 'rhs', opts),
  689. get_mapargs(mapmode, 'lhs'))
  690. meths.del_keymap(mapmode, 'lhs')
  691. eq({}, get_mapargs(mapmode, 'lhs'))
  692. end)
  693. end
  694. end
  695. it('can make lua mappings', function()
  696. eq(0, exec_lua [[
  697. GlobalCount = 0
  698. vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
  699. return GlobalCount
  700. ]])
  701. feed('asdf\n')
  702. eq(1, exec_lua[[return GlobalCount]])
  703. end)
  704. it (':map command shows lua mapping correctly', function()
  705. exec_lua [[
  706. vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end })
  707. ]]
  708. assert.truthy(string.match(exec_lua[[return vim.api.nvim_exec(':nmap asdf', true)]],
  709. "^\nn asdf <Lua %d+>"))
  710. end)
  711. it ('mapcheck() returns lua mapping correctly', function()
  712. exec_lua [[
  713. vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end })
  714. ]]
  715. assert.truthy(string.match(funcs.mapcheck('asdf', 'n'),
  716. "^<Lua %d+>"))
  717. end)
  718. it ('maparg() returns lua mapping correctly', function()
  719. eq(0, exec_lua([[
  720. GlobalCount = 0
  721. vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
  722. return GlobalCount
  723. ]]))
  724. assert.truthy(string.match(funcs.maparg('asdf', 'n'), "^<Lua %d+>"))
  725. local mapargs = funcs.maparg('asdf', 'n', false, true)
  726. mapargs.callback = nil
  727. mapargs.lhsraw = nil
  728. mapargs.lhsrawalt = nil
  729. eq(generate_mapargs('n', 'asdf', nil, {sid=sid_lua}), mapargs)
  730. eq(1, exec_lua([[
  731. vim.fn.maparg('asdf', 'n', false, true).callback()
  732. return GlobalCount
  733. ]]))
  734. exec([[
  735. call maparg('asdf', 'n', v:false, v:true).callback()
  736. ]])
  737. eq(2, exec_lua([[return GlobalCount]]))
  738. end)
  739. it('can make lua expr mappings replacing keycodes', function()
  740. exec_lua [[
  741. vim.api.nvim_set_keymap ('n', 'aa', '', {callback = function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end, expr = true, replace_keycodes = true })
  742. ]]
  743. feed('aa')
  744. eq({'π<M-π>foo<'}, meths.buf_get_lines(0, 0, -1, false))
  745. end)
  746. it('can make lua expr mappings without replacing keycodes', function()
  747. exec_lua [[
  748. vim.api.nvim_set_keymap ('i', 'aa', '', {callback = function() return '<space>' end, expr = true })
  749. ]]
  750. feed('iaa<esc>')
  751. eq({'<space>'}, meths.buf_get_lines(0, 0, -1, false))
  752. end)
  753. it('lua expr mapping returning nil is equivalent to returning an empty string', function()
  754. exec_lua [[
  755. vim.api.nvim_set_keymap ('i', 'aa', '', {callback = function() return nil end, expr = true })
  756. ]]
  757. feed('iaa<esc>')
  758. eq({''}, meths.buf_get_lines(0, 0, -1, false))
  759. end)
  760. it('does not reset pum in lua mapping', function()
  761. eq(0, exec_lua [[
  762. VisibleCount = 0
  763. vim.api.nvim_set_keymap ('i', '<F2>', '', {callback = function() VisibleCount = VisibleCount + vim.fn.pumvisible() end})
  764. return VisibleCount
  765. ]])
  766. feed('i<C-X><C-V><F2><F2><esc>')
  767. eq(2, exec_lua[[return VisibleCount]])
  768. end)
  769. it('can overwrite lua mappings', function()
  770. eq(0, exec_lua [[
  771. GlobalCount = 0
  772. vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
  773. return GlobalCount
  774. ]])
  775. feed('asdf\n')
  776. eq(1, exec_lua[[return GlobalCount]])
  777. exec_lua [[
  778. vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount - 1 end })
  779. ]]
  780. feed('asdf\n')
  781. eq(0, exec_lua[[return GlobalCount]])
  782. end)
  783. it('can unmap lua mappings', function()
  784. eq(0, exec_lua [[
  785. GlobalCount = 0
  786. vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
  787. return GlobalCount
  788. ]])
  789. feed('asdf\n')
  790. eq(1, exec_lua[[return GlobalCount]])
  791. exec_lua [[
  792. vim.api.nvim_del_keymap('n', 'asdf' )
  793. ]]
  794. feed('asdf\n')
  795. eq(1, exec_lua[[return GlobalCount]])
  796. eq('\nNo mapping found', helpers.exec_capture('nmap asdf'))
  797. end)
  798. it('no double-free when unmapping simplifiable lua mappings', function()
  799. eq(0, exec_lua [[
  800. GlobalCount = 0
  801. vim.api.nvim_set_keymap('n', '<C-I>', '', {callback = function() GlobalCount = GlobalCount + 1 end })
  802. return GlobalCount
  803. ]])
  804. feed('<C-I>\n')
  805. eq(1, exec_lua[[return GlobalCount]])
  806. exec_lua [[
  807. vim.api.nvim_del_keymap('n', '<C-I>')
  808. ]]
  809. feed('<C-I>\n')
  810. eq(1, exec_lua[[return GlobalCount]])
  811. eq('\nNo mapping found', helpers.exec_capture('nmap <C-I>'))
  812. end)
  813. it('can set descriptions on mappings', function()
  814. meths.set_keymap('n', 'lhs', 'rhs', {desc="map description"})
  815. eq(generate_mapargs('n', 'lhs', 'rhs', {desc="map description"}), get_mapargs('n', 'lhs'))
  816. eq("\nn lhs rhs\n map description",
  817. helpers.exec_capture("nmap lhs"))
  818. end)
  819. end)
  820. describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
  821. before_each(clear)
  822. -- nvim_set_keymap is implemented as a wrapped call to nvim_buf_set_keymap,
  823. -- so its tests also effectively test nvim_buf_set_keymap
  824. -- here, we mainly test for buffer specificity and other special cases
  825. -- switch to the given buffer, abandoning any changes in the current buffer
  826. local function switch_to_buf(bufnr)
  827. command(bufnr..'buffer!')
  828. end
  829. -- `set hidden`, then create two buffers and return their bufnr's
  830. -- If start_from_first is truthy, the first buffer will be open when
  831. -- the function returns; if falsy, the second buffer will be open.
  832. local function make_two_buffers(start_from_first)
  833. command('set hidden')
  834. local first_buf = meths.call_function('bufnr', {'%'})
  835. command('new')
  836. local second_buf = meths.call_function('bufnr', {'%'})
  837. neq(second_buf, first_buf) -- sanity check
  838. if start_from_first then
  839. switch_to_buf(first_buf)
  840. end
  841. return first_buf, second_buf
  842. end
  843. it('rejects negative bufnr values', function()
  844. eq('Wrong type for argument 1 when calling nvim_buf_set_keymap, expecting Buffer',
  845. pcall_err(bufmeths.set_keymap, -1, '', 'lhs', 'rhs', {}))
  846. end)
  847. it('can set mappings active in the current buffer but not others', function()
  848. local first, second = make_two_buffers(true)
  849. bufmeths.set_keymap(0, '', 'lhs', 'irhs<Esc>', {})
  850. command('normal lhs')
  851. eq({'rhs'}, bufmeths.get_lines(0, 0, 1, 1))
  852. -- mapping should have no effect in new buffer
  853. switch_to_buf(second)
  854. command('normal lhs')
  855. eq({''}, bufmeths.get_lines(0, 0, 1, 1))
  856. -- mapping should remain active in old buffer
  857. switch_to_buf(first)
  858. command('normal ^lhs')
  859. eq({'rhsrhs'}, bufmeths.get_lines(0, 0, 1, 1))
  860. end)
  861. it('can set local mappings in buffer other than current', function()
  862. local first = make_two_buffers(false)
  863. bufmeths.set_keymap(first, '', 'lhs', 'irhs<Esc>', {})
  864. -- shouldn't do anything
  865. command('normal lhs')
  866. eq({''}, bufmeths.get_lines(0, 0, 1, 1))
  867. -- should take effect
  868. switch_to_buf(first)
  869. command('normal lhs')
  870. eq({'rhs'}, bufmeths.get_lines(0, 0, 1, 1))
  871. end)
  872. it('can disable mappings made in another buffer, inside that buffer', function()
  873. local first = make_two_buffers(false)
  874. bufmeths.set_keymap(first, '', 'lhs', 'irhs<Esc>', {})
  875. bufmeths.del_keymap(first, '', 'lhs')
  876. switch_to_buf(first)
  877. -- shouldn't do anything
  878. command('normal lhs')
  879. eq({''}, bufmeths.get_lines(0, 0, 1, 1))
  880. end)
  881. it("can't disable mappings given wrong buffer handle", function()
  882. local first, second = make_two_buffers(false)
  883. bufmeths.set_keymap(first, '', 'lhs', 'irhs<Esc>', {})
  884. eq('E31: No such mapping',
  885. pcall_err(bufmeths.del_keymap, second, '', 'lhs'))
  886. -- should still work
  887. switch_to_buf(first)
  888. command('normal lhs')
  889. eq({'rhs'}, bufmeths.get_lines(0, 0, 1, 1))
  890. end)
  891. it("does not crash when setting keymap in a non-existing buffer #13541", function()
  892. pcall_err(bufmeths.set_keymap, 100, '', 'lsh', 'irhs<Esc>', {})
  893. helpers.assert_alive()
  894. end)
  895. it('can make lua mappings', function()
  896. eq(0, exec_lua [[
  897. GlobalCount = 0
  898. vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
  899. return GlobalCount
  900. ]])
  901. feed('asdf\n')
  902. eq(1, exec_lua[[return GlobalCount]])
  903. end)
  904. it('can make lua expr mappings replacing keycodes', function()
  905. exec_lua [[
  906. vim.api.nvim_buf_set_keymap (0, 'n', 'aa', '', {callback = function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end, expr = true, replace_keycodes = true })
  907. ]]
  908. feed('aa')
  909. eq({'π<M-π>foo<'}, meths.buf_get_lines(0, 0, -1, false))
  910. end)
  911. it('can make lua expr mappings without replacing keycodes', function()
  912. exec_lua [[
  913. vim.api.nvim_buf_set_keymap (0, 'i', 'aa', '', {callback = function() return '<space>' end, expr = true })
  914. ]]
  915. feed('iaa<esc>')
  916. eq({'<space>'}, meths.buf_get_lines(0, 0, -1, false))
  917. end)
  918. it('can overwrite lua mappings', function()
  919. eq(0, exec_lua [[
  920. GlobalCount = 0
  921. vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
  922. return GlobalCount
  923. ]])
  924. feed('asdf\n')
  925. eq(1, exec_lua[[return GlobalCount]])
  926. exec_lua [[
  927. vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount - 1 end })
  928. ]]
  929. feed('asdf\n')
  930. eq(0, exec_lua[[return GlobalCount]])
  931. end)
  932. it('can unmap lua mappings', function()
  933. eq(0, exec_lua [[
  934. GlobalCount = 0
  935. vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
  936. return GlobalCount
  937. ]])
  938. feed('asdf\n')
  939. eq(1, exec_lua[[return GlobalCount]])
  940. exec_lua [[
  941. vim.api.nvim_buf_del_keymap(0, 'n', 'asdf' )
  942. ]]
  943. feed('asdf\n')
  944. eq(1, exec_lua[[return GlobalCount]])
  945. eq('\nNo mapping found', helpers.exec_capture('nmap asdf'))
  946. end)
  947. it('no double-free when unmapping simplifiable lua mappings', function()
  948. eq(0, exec_lua [[
  949. GlobalCount = 0
  950. vim.api.nvim_buf_set_keymap(0, 'n', '<C-I>', '', {callback = function() GlobalCount = GlobalCount + 1 end })
  951. return GlobalCount
  952. ]])
  953. feed('<C-I>\n')
  954. eq(1, exec_lua[[return GlobalCount]])
  955. exec_lua [[
  956. vim.api.nvim_buf_del_keymap(0, 'n', '<C-I>')
  957. ]]
  958. feed('<C-I>\n')
  959. eq(1, exec_lua[[return GlobalCount]])
  960. eq('\nNo mapping found', helpers.exec_capture('nmap <C-I>'))
  961. end)
  962. end)