123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745 |
- local t = require('test.testutil')
- local n = require('test.functional.testnvim')()
- local Screen = require('test.functional.ui.screen')
- local clear = n.clear
- local feed = n.feed
- local eq = t.eq
- local expect = n.expect
- local eval = n.eval
- local fn = n.fn
- local insert = n.insert
- local write_file = t.write_file
- local exc_exec = n.exc_exec
- local command = n.command
- describe('mappings with <Cmd>', function()
- local screen
- local tmpfile = 'X_ex_cmds_cmd_map'
- local function cmdmap(lhs, rhs)
- command('noremap ' .. lhs .. ' <Cmd>' .. rhs .. '<cr>')
- command('noremap! ' .. lhs .. ' <Cmd>' .. rhs .. '<cr>')
- end
- before_each(function()
- clear()
- screen = Screen.new(65, 8)
- screen:set_default_attr_ids({
- [1] = { bold = true, foreground = Screen.colors.Blue1 },
- [2] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red },
- [3] = { bold = true, foreground = Screen.colors.SeaGreen4 },
- [4] = { bold = true },
- [5] = { foreground = Screen.colors.Black, background = Screen.colors.LightGrey },
- [6] = { foreground = Screen.colors.Blue1 },
- [7] = { bold = true, reverse = true },
- [8] = { background = Screen.colors.WebGray },
- [9] = { background = Screen.colors.LightMagenta },
- [10] = { foreground = Screen.colors.Red },
- })
- cmdmap('<F3>', 'let m = mode(1)')
- cmdmap('<F4>', 'normal! ww')
- cmdmap('<F5>', 'normal! "ay')
- cmdmap('<F6>', 'throw "very error"')
- command([[
- function! TextObj()
- if mode() !=# "v"
- normal! v
- end
- call cursor(1,3)
- normal! o
- call cursor(2,4)
- endfunction]])
- cmdmap('<F7>', 'call TextObj()')
- insert([[
- some short lines
- of test text]])
- feed('gg')
- cmdmap('<F8>', 'startinsert')
- cmdmap('<F9>', 'stopinsert')
- command('abbr foo <Cmd>let g:y = 17<cr>bar')
- end)
- after_each(function()
- os.remove(tmpfile)
- end)
- it('can be displayed', function()
- command('map <F3>')
- screen:expect([[
- ^some short lines |
- of test text |
- {1:~ }|*5
- {6:<F3>} {6:*} {6:<Cmd>}let m = mode(1){6:<CR>} |
- ]])
- end)
- it('handles invalid mappings', function()
- command('let x = 0')
- command('noremap <F3> <Cmd><Cmd>let x = 1<cr>')
- feed('<F3>')
- screen:expect([[
- ^some short lines |
- of test text |
- {1:~ }|*5
- {2:E1136: <Cmd> mapping must end with <CR> before second <Cmd>} |
- ]])
- command('noremap <F3> <Cmd>let x = 3')
- feed('<F3>')
- screen:expect([[
- ^some short lines |
- of test text |
- {1:~ }|*5
- {2:E1255: <Cmd> mapping must end with <CR>} |
- ]])
- eq(0, eval('x'))
- end)
- it('allows special keys and modifiers', function()
- command('noremap <F3> <Cmd>normal! <Down><CR>')
- feed('<F3>')
- screen:expect([[
- some short lines |
- ^of test text |
- {1:~ }|*5
- |
- ]])
- command('noremap <F3> <Cmd>normal! <C-Right><CR>')
- feed('<F3>')
- screen:expect([[
- some short lines |
- of ^test text |
- {1:~ }|*5
- |
- ]])
- end)
- it('handles string containing K_SPECIAL (0x80) bytes correctly', function()
- command([[noremap <F3> <Cmd>let g:str = 'foo…bar'<CR>]])
- feed('<F3>')
- eq('foo…bar', eval('g:str'))
- local str = eval([["foo\<D-…>bar"]])
- command([[noremap <F3> <Cmd>let g:str = ']] .. str .. [['<CR>]])
- feed('<F3>')
- eq(str, eval('g:str'))
- command([[noremap <F3> <Cmd>let g:str = 'foo<D-…>bar'<CR>]])
- feed('<F3>')
- eq(str, eval('g:str'))
- end)
- it('works in various modes and sees correct `mode()` value', function()
- -- normal mode
- feed('<F3>')
- eq('n', eval('m'))
- -- visual mode
- feed('v<F3>')
- eq('v', eval('m'))
- -- didn't leave visual mode
- eq('v', eval('mode(1)'))
- feed('<esc>')
- eq('n', eval('mode(1)'))
- -- visual mapping in select mode
- feed('gh<F3>')
- eq('v', eval('m'))
- -- didn't leave select mode
- eq('s', eval('mode(1)'))
- feed('<esc>')
- eq('n', eval('mode(1)'))
- -- select mode mapping
- command('snoremap <F3> <Cmd>let m = mode(1)<cr>')
- feed('gh<F3>')
- eq('s', eval('m'))
- -- didn't leave select mode
- eq('s', eval('mode(1)'))
- feed('<esc>')
- eq('n', eval('mode(1)'))
- -- operator-pending mode
- feed('d<F3>')
- eq('no', eval('m'))
- -- did leave operator-pending mode
- eq('n', eval('mode(1)'))
- --insert mode
- feed('i<F3>')
- eq('i', eval('m'))
- eq('i', eval('mode(1)'))
- -- replace mode
- feed('<Ins><F3>')
- eq('R', eval('m'))
- eq('R', eval('mode(1)'))
- feed('<esc>')
- eq('n', eval('mode(1)'))
- -- virtual replace mode
- feed('gR<F3>')
- eq('Rv', eval('m'))
- eq('Rv', eval('mode(1)'))
- feed('<esc>')
- eq('n', eval('mode(1)'))
- -- langmap works, but is not distinguished in mode(1)
- feed(':set iminsert=1<cr>i<F3>')
- eq('i', eval('m'))
- eq('i', eval('mode(1)'))
- feed('<esc>')
- eq('n', eval('mode(1)'))
- feed(':<F3>')
- eq('c', eval('m'))
- eq('c', eval('mode(1)'))
- feed('<esc>')
- eq('n', eval('mode(1)'))
- -- terminal mode
- command('tnoremap <F3> <Cmd>let m = mode(1)<cr>')
- command('split | terminal')
- feed('i')
- eq('t', eval('mode(1)'))
- feed('<F3>')
- eq('t', eval('m'))
- eq('t', eval('mode(1)'))
- end)
- it('works in normal mode', function()
- cmdmap('<F2>', 'let s = [mode(1), v:count, v:register]')
- -- check v:count and v:register works
- feed('<F2>')
- eq({ 'n', 0, '"' }, eval('s'))
- feed('7<F2>')
- eq({ 'n', 7, '"' }, eval('s'))
- feed('"e<F2>')
- eq({ 'n', 0, 'e' }, eval('s'))
- feed('5"k<F2>')
- eq({ 'n', 5, 'k' }, eval('s'))
- feed('"+2<F2>')
- eq({ 'n', 2, '+' }, eval('s'))
- -- text object enters visual mode
- feed('<F7>')
- screen:expect([[
- so{5:me short lines} |
- {5:of }^test text |
- {1:~ }|*5
- {4:-- VISUAL --} |
- ]])
- feed('<esc>')
- -- startinsert
- feed('<F8>')
- eq('i', eval('mode(1)'))
- feed('<esc>')
- eq('n', eval('mode(1)'))
- cmdmap(',a', 'call feedkeys("aalpha") \\| let g:a = getline(2)')
- cmdmap(',b', 'call feedkeys("abeta", "x") \\| let g:b = getline(2)')
- feed(',a<F3>')
- screen:expect([[
- some short lines |
- of alpha^test text |
- {1:~ }|*5
- {4:-- INSERT --} |
- ]])
- -- feedkeys were not executed immediately
- eq({ 'n', 'of test text' }, eval('[m,a]'))
- eq('i', eval('mode(1)'))
- feed('<esc>')
- feed(',b<F3>')
- screen:expect([[
- some short lines |
- of alphabet^atest text |
- {1:~ }|*5
- |
- ]])
- -- feedkeys(..., 'x') was executed immediately, but insert mode gets aborted
- eq({ 'n', 'of alphabetatest text' }, eval('[m,b]'))
- eq('n', eval('mode(1)'))
- end)
- it('works in :normal command', function()
- command('noremap ,x <Cmd>call append(1, "xx")\\| call append(1, "aa")<cr>')
- command('noremap ,f <Cmd>nosuchcommand<cr>')
- command('noremap ,e <Cmd>throw "very error"\\| call append(1, "yy")<cr>')
- command('noremap ,m <Cmd>echoerr "The message."\\| call append(1, "zz")<cr>')
- command(
- 'noremap ,w <Cmd>for i in range(5)\\|if i==1\\|echoerr "Err"\\|endif\\|call append(1, i)\\|endfor<cr>'
- )
- feed(':normal ,x<cr>')
- screen:expect([[
- ^some short lines |
- aa |
- xx |
- of test text |
- {1:~ }|*3
- :normal ,x |
- ]])
- eq('Vim:E492: Not an editor command: nosuchcommand', exc_exec('normal ,f'))
- eq('very error', exc_exec('normal ,e'))
- eq('Vim(echoerr):The message.', exc_exec('normal ,m'))
- feed('w')
- screen:expect([[
- some ^short lines |
- aa |
- xx |
- of test text |
- {1:~ }|*3
- :normal ,x |
- ]])
- command(':%d')
- eq('Vim(echoerr):Err', exc_exec('normal ,w'))
- screen:expect([[
- ^ |
- 0 |
- {1:~ }|*5
- --No lines in buffer-- |
- ]])
- command(':%d')
- feed(':normal ,w<cr>')
- screen:expect([[
- ^ |
- 4 |
- 3 |
- 2 |
- 1 |
- 0 |
- {1:~ }|
- {2:Err} |
- ]])
- end)
- it('works in visual mode', function()
- -- can extend visual mode
- feed('v<F4>')
- screen:expect([[
- {5:some short }^lines |
- of test text |
- {1:~ }|*5
- {4:-- VISUAL --} |
- ]])
- eq('v', fn.mode(1))
- -- can invoke operator, ending visual mode
- feed('<F5>')
- eq('n', fn.mode(1))
- eq({ 'some short l' }, fn.getreg('a', 1, 1))
- -- error doesn't interrupt visual mode
- feed('ggvw<F6>')
- screen:expect([[
- {5:some }short lines |
- of test text |
- {1:~ }|*2
- {7: }|
- {2:Error detected while processing :} |
- {2:E605: Exception not caught: very error} |
- {3:Press ENTER or type command to continue}^ |
- ]])
- feed('<cr>')
- eq('E605: Exception not caught: very error', eval('v:errmsg'))
- -- still in visual mode, <cr> was consumed by the error prompt
- screen:expect([[
- {5:some }^short lines |
- of test text |
- {1:~ }|*5
- {4:-- VISUAL --} |
- ]])
- eq('v', fn.mode(1))
- feed('<F7>')
- screen:expect([[
- so{5:me short lines} |
- {5:of }^test text |
- {1:~ }|*5
- {4:-- VISUAL --} |
- ]])
- eq('v', fn.mode(1))
- -- startinsert gives "-- (insert) VISUAL --" mode
- feed('<F8>')
- screen:expect([[
- so{5:me short lines} |
- {5:of }^test text |
- {1:~ }|*5
- {4:-- (insert) VISUAL --} |
- ]])
- eq('v', eval('mode(1)'))
- feed('<esc>')
- eq('i', eval('mode(1)'))
- end)
- it('works in select mode', function()
- command('snoremap <F1> <cmd>throw "very error"<cr>')
- command('snoremap <F2> <cmd>normal! <c-g>"by<cr>')
- -- can extend select mode
- feed('gh<F4>')
- screen:expect([[
- {5:some short }^lines |
- of test text |
- {1:~ }|*5
- {4:-- SELECT --} |
- ]])
- eq('s', fn.mode(1))
- -- visual mapping in select mode restart select mode after operator
- feed('<F5>')
- eq('s', fn.mode(1))
- eq({ 'some short l' }, fn.getreg('a', 1, 1))
- -- select mode mapping works, and does not restart select mode
- feed('<F2>')
- eq('n', fn.mode(1))
- eq({ 'some short l' }, fn.getreg('b', 1, 1))
- -- error doesn't interrupt temporary visual mode
- feed('<esc>ggvw<c-g><F6>')
- screen:expect([[
- {5:some }short lines |
- of test text |
- {1:~ }|*2
- {7: }|
- {2:Error detected while processing :} |
- {2:E605: Exception not caught: very error} |
- {3:Press ENTER or type command to continue}^ |
- ]])
- feed('<cr>')
- eq('E605: Exception not caught: very error', eval('v:errmsg'))
- -- still in visual mode, <cr> was consumed by the error prompt
- screen:expect([[
- {5:some }^short lines |
- of test text |
- {1:~ }|*5
- {4:-- VISUAL --} |
- ]])
- -- quirk: restoration of select mode is not performed
- eq('v', fn.mode(1))
- -- error doesn't interrupt select mode
- feed('<esc>ggvw<c-g><F1>')
- screen:expect([[
- {5:some }short lines |
- of test text |
- {1:~ }|*2
- {7: }|
- {2:Error detected while processing :} |
- {2:E605: Exception not caught: very error} |
- {3:Press ENTER or type command to continue}^ |
- ]])
- feed('<cr>')
- eq('E605: Exception not caught: very error', eval('v:errmsg'))
- -- still in select mode, <cr> was consumed by the error prompt
- screen:expect([[
- {5:some }^short lines |
- of test text |
- {1:~ }|*5
- {4:-- SELECT --} |
- ]])
- -- quirk: restoration of select mode is not performed
- eq('s', fn.mode(1))
- feed('<F7>')
- screen:expect([[
- so{5:me short lines} |
- {5:of }^test text |
- {1:~ }|*5
- {4:-- SELECT --} |
- ]])
- eq('s', fn.mode(1))
- -- startinsert gives "-- SELECT (insert) --" mode
- feed('<F8>')
- screen:expect([[
- so{5:me short lines} |
- {5:of }^test text |
- {1:~ }|*5
- {4:-- (insert) SELECT --} |
- ]])
- eq('s', eval('mode(1)'))
- feed('<esc>')
- eq('i', eval('mode(1)'))
- end)
- it('works in operator-pending mode', function()
- feed('d<F4>')
- expect([[
- lines
- of test text]])
- eq({ 'some short ' }, fn.getreg('"', 1, 1))
- feed('.')
- expect([[
- test text]])
- eq({ 'lines', 'of ' }, fn.getreg('"', 1, 1))
- feed('uu')
- expect([[
- some short lines
- of test text]])
- -- error aborts operator-pending, operator not performed
- feed('d<F6>')
- screen:expect([[
- some short lines |
- of test text |
- {1:~ }|*2
- {7: }|
- {2:Error detected while processing :} |
- {2:E605: Exception not caught: very error} |
- {3:Press ENTER or type command to continue}^ |
- ]])
- feed('<cr>')
- eq('E605: Exception not caught: very error', eval('v:errmsg'))
- expect([[
- some short lines
- of test text]])
- feed('"bd<F7>')
- expect([[
- soest text]])
- eq({ 'me short lines', 'of t' }, fn.getreg('b', 1, 1))
- -- startinsert aborts operator
- feed('d<F8>')
- eq('i', eval('mode(1)'))
- expect([[
- soest text]])
- end)
- it('works in insert mode', function()
- -- works the same as <c-o>w<c-o>w
- feed('iindeed <F4>little ')
- screen:expect([[
- indeed some short little ^lines |
- of test text |
- {1:~ }|*5
- {4:-- INSERT --} |
- ]])
- feed('<F6>')
- screen:expect([[
- indeed some short little lines |
- of test text |
- {1:~ }|*2
- {7: }|
- {2:Error detected while processing :} |
- {2:E605: Exception not caught: very error} |
- {3:Press ENTER or type command to continue}^ |
- ]])
- feed('<cr>')
- eq('E605: Exception not caught: very error', eval('v:errmsg'))
- -- still in insert
- screen:expect([[
- indeed some short little ^lines |
- of test text |
- {1:~ }|*5
- {4:-- INSERT --} |
- ]])
- eq('i', eval('mode(1)'))
- -- When entering visual mode from InsertEnter autocmd, an async event, or
- -- a <cmd> mapping, vim ends up in undocumented "INSERT VISUAL" mode. If a
- -- vim patch decides to disable this mode, this test is expected to fail.
- feed('<F7>stuff ')
- screen:expect([[
- in{5:deed some short little lines} |
- {5:of stuff }^test text |
- {1:~ }|*5
- {4:-- INSERT VISUAL --} |
- ]])
- expect([[
- indeed some short little lines
- of stuff test text]])
- feed('<F5>')
- eq({ 'deed some short little lines', 'of stuff t' }, fn.getreg('a', 1, 1))
- -- still in insert
- screen:expect([[
- in^deed some short little lines |
- of stuff test text |
- {1:~ }|*5
- {4:-- INSERT --} |
- ]])
- eq('i', eval('mode(1)'))
- -- also works as part of abbreviation
- feed('<space>foo ')
- screen:expect([[
- in bar ^deed some short little lines |
- of stuff test text |
- {1:~ }|*5
- {4:-- INSERT --} |
- ]])
- eq(17, eval('g:y'))
- -- :startinsert does nothing
- feed('<F8>')
- eq('i', eval('mode(1)'))
- -- :stopinsert works
- feed('<F9>')
- eq('n', eval('mode(1)'))
- end)
- it('works in insert completion (Ctrl-X) mode', function()
- feed('os<c-x><c-n>')
- screen:expect([[
- some short lines |
- some^ |
- {8:some } |
- {9:short }{1: }|
- {1:~ }|*3
- {4:-- Keyword Local completion (^N^P) }{3:match 1 of 2} |
- ]])
- feed('<f3>')
- eq('ic', eval('m'))
- -- ensure a redraw, this would have moved the cursor
- -- instead if CTRL-X mode was left.
- feed('<up>')
- screen:expect([[
- some short lines |
- some^ |
- {9:some } |
- {9:short }{1: }|
- {1:~ }|*3
- {4:-- Keyword Local completion (^N^P) }{10:Back at original} |
- ]])
- end)
- it('works in cmdline mode', function()
- feed(':text<F3>')
- eq('c', eval('m'))
- -- didn't leave cmdline mode
- eq('c', eval('mode(1)'))
- feed('<cr>')
- eq('n', eval('mode(1)'))
- screen:expect([[
- ^some short lines |
- of test text |
- {1:~ }|*5
- {2:E492: Not an editor command: text} |
- ]])
- feed(':echo 2<F6>')
- screen:expect([[
- some short lines |
- of test text |
- {1:~ }|
- {7: }|
- :echo 2 |
- {2:Error detected while processing :} |
- {2:E605: Exception not caught: very error} |
- :echo 2^ |
- ]])
- eq('E605: Exception not caught: very error', eval('v:errmsg'))
- -- didn't leave cmdline mode
- eq('c', eval('mode(1)'))
- feed('+2<cr>')
- screen:expect([[
- some short lines |
- of test text |
- {7: }|
- :echo 2 |
- {2:Error detected while processing :} |
- {2:E605: Exception not caught: very error} |
- 4 |
- {3:Press ENTER or type command to continue}^ |
- ]])
- -- however, message scrolling may cause extra CR prompt
- -- This is consistent with output from async events.
- feed('<cr>')
- screen:expect([[
- ^some short lines |
- of test text |
- {1:~ }|*5
- |
- ]])
- eq('n', eval('mode(1)'))
- feed(':let g:x = 3<F4>')
- screen:expect([[
- some short lines |
- of test text |
- {1:~ }|*5
- :let g:x = 3^ |
- ]])
- feed('+2<cr>')
- -- cursor was moved in the background
- screen:expect([[
- some short ^lines |
- of test text |
- {1:~ }|*5
- :let g:x = 3+2 |
- ]])
- eq(5, eval('g:x'))
- feed(':let g:y = 7<F8>')
- screen:expect([[
- some short lines |
- of test text |
- {1:~ }|*5
- :let g:y = 7^ |
- ]])
- eq('c', eval('mode(1)'))
- feed('+2<cr>')
- -- startinsert takes effect after leaving cmdline mode
- screen:expect([[
- some short ^lines |
- of test text |
- {1:~ }|*5
- {4:-- INSERT --} |
- ]])
- eq('i', eval('mode(1)'))
- eq(9, eval('g:y'))
- end)
- it("doesn't crash when invoking cmdline mode recursively #8859", function()
- cmdmap('<F2>', 'norm! :foo')
- feed(':bar')
- screen:expect([[
- some short lines |
- of test text |
- {1:~ }|*5
- :bar^ |
- ]])
- feed('<f2>x')
- screen:expect([[
- some short lines |
- of test text |
- {1:~ }|*5
- :barx^ |
- ]])
- end)
- it('works with <SID> mappings', function()
- command('new!')
- write_file(
- tmpfile,
- [[
- map <f2> <Cmd>call <SID>do_it()<Cr>
- function! s:do_it()
- let g:x = 10
- endfunction
- ]]
- )
- command('source ' .. tmpfile)
- feed('<f2>')
- eq('', eval('v:errmsg'))
- eq(10, eval('g:x'))
- end)
- end)
|