keymap_spec.lua 44 KB

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