diff options
| author | James McCoy <jamessan@jamessan.com> | 2018-03-28 21:52:06 -0400 |
|---|---|---|
| committer | James McCoy <jamessan@jamessan.com> | 2018-03-28 21:54:39 -0400 |
| commit | 79f9c2d9c650ceab27cdc6707fd6d7fa1de29fc1 (patch) | |
| tree | 4e0589d75801f3ff6a9678f84f5009102766661e /test/functional/ex_cmds | |
| parent | 4403864da3c48412595d439f36458d1e6ccfc49f (diff) | |
| parent | 3f3de9b1a95d273463a87516365510dbffcaf3d2 (diff) | |
| download | rneovim-79f9c2d9c650ceab27cdc6707fd6d7fa1de29fc1.tar.gz rneovim-79f9c2d9c650ceab27cdc6707fd6d7fa1de29fc1.tar.bz2 rneovim-79f9c2d9c650ceab27cdc6707fd6d7fa1de29fc1.zip | |
Merge branch 'master' into yagebu/option-fixes
Diffstat (limited to 'test/functional/ex_cmds')
| -rw-r--r-- | test/functional/ex_cmds/bang_filter_spec.lua | 51 | ||||
| -rw-r--r-- | test/functional/ex_cmds/cd_spec.lua | 46 | ||||
| -rw-r--r-- | test/functional/ex_cmds/cmd_map_spec.lua | 771 | ||||
| -rw-r--r-- | test/functional/ex_cmds/ctrl_c_spec.lua | 2 | ||||
| -rw-r--r-- | test/functional/ex_cmds/dict_notifications_spec.lua | 2 | ||||
| -rw-r--r-- | test/functional/ex_cmds/drop_spec.lua | 32 | ||||
| -rw-r--r-- | test/functional/ex_cmds/echo_spec.lua | 321 | ||||
| -rw-r--r-- | test/functional/ex_cmds/highlight_spec.lua | 43 | ||||
| -rw-r--r-- | test/functional/ex_cmds/map_spec.lua | 28 | ||||
| -rw-r--r-- | test/functional/ex_cmds/menu_spec.lua | 571 | ||||
| -rw-r--r-- | test/functional/ex_cmds/mksession_spec.lua | 3 | ||||
| -rw-r--r-- | test/functional/ex_cmds/mkview_spec.lua | 67 | ||||
| -rw-r--r-- | test/functional/ex_cmds/quickfix_commands_spec.lua | 28 | ||||
| -rw-r--r-- | test/functional/ex_cmds/sign_spec.lua | 4 | ||||
| -rw-r--r-- | test/functional/ex_cmds/write_spec.lua | 31 |
15 files changed, 1898 insertions, 102 deletions
diff --git a/test/functional/ex_cmds/bang_filter_spec.lua b/test/functional/ex_cmds/bang_filter_spec.lua deleted file mode 100644 index aaec983b73..0000000000 --- a/test/functional/ex_cmds/bang_filter_spec.lua +++ /dev/null @@ -1,51 +0,0 @@ --- Specs for bang/filter commands - -local helpers = require('test.functional.helpers')(after_each) -local feed, command, clear = helpers.feed, helpers.command, helpers.clear -local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir - -if helpers.pending_win32(pending) then return end - -local Screen = require('test.functional.ui.screen') - - -describe('issues', function() - local screen - - before_each(function() - clear() - rmdir('bang_filter_spec') - mkdir('bang_filter_spec') - write_file('bang_filter_spec/f1', 'f1') - write_file('bang_filter_spec/f2', 'f2') - write_file('bang_filter_spec/f3', 'f3') - screen = Screen.new() - screen:attach() - end) - - after_each(function() - rmdir('bang_filter_spec') - end) - - it('#3269 Last line of shell output is not truncated', function() - command([[nnoremap <silent>\l :!ls bang_filter_spec<cr>]]) - feed([[\l]]) - screen:expect([[ - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - :!ls bang_filter_spec | - | - f1 | - f2 | - f3 | - Press ENTER or type command to continue^ | - ]]) - end) - -end) diff --git a/test/functional/ex_cmds/cd_spec.lua b/test/functional/ex_cmds/cd_spec.lua index 059cb26d5d..bc2b365b30 100644 --- a/test/functional/ex_cmds/cd_spec.lua +++ b/test/functional/ex_cmds/cd_spec.lua @@ -8,8 +8,7 @@ local call = helpers.call local clear = helpers.clear local command = helpers.command local exc_exec = helpers.exc_exec - -if helpers.pending_win32(pending) then return end +local pathsep = helpers.get_pathsep() -- These directories will be created for testing local directories = { @@ -75,8 +74,8 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, lwd(globalwin, tabnr)) -- Window with local dir reports as such - eq(globalDir .. '/' .. directories.window, cwd(localwin)) - eq(globalDir .. '/' .. directories.window, cwd(localwin, tabnr)) + eq(globalDir .. pathsep .. directories.window, cwd(localwin)) + eq(globalDir .. pathsep .. directories.window, cwd(localwin, tabnr)) eq(1, lwd(localwin)) eq(1, lwd(localwin, tabnr)) @@ -86,7 +85,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, lwd(globalwin, tabnr)) -- From new tab page, local window reports as such - eq(globalDir .. '/' .. directories.window, cwd(localwin, tabnr)) + eq(globalDir .. pathsep .. directories.window, cwd(localwin, tabnr)) eq(1, lwd(localwin, tabnr)) end) @@ -109,14 +108,14 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, lwd(-1, globaltab)) -- new tab reports local - eq(globalDir .. '/' .. directories.tab, cwd(-1, 0)) - eq(globalDir .. '/' .. directories.tab, cwd(-1, localtab)) + eq(globalDir .. pathsep .. directories.tab, cwd(-1, 0)) + eq(globalDir .. pathsep .. directories.tab, cwd(-1, localtab)) eq(1, lwd(-1, 0)) eq(1, lwd(-1, localtab)) command('tabnext') -- From original tab page, local reports as such - eq(globalDir .. '/' .. directories.tab, cwd(-1, localtab)) + eq(globalDir .. pathsep .. directories.tab, cwd(-1, localtab)) eq(1, lwd(-1, localtab)) end) end) @@ -147,17 +146,17 @@ for _, cmd in ipairs {'cd', 'chdir'} do -- Create a new tab and change directory command('tabnew') command('silent t' .. cmd .. ' ' .. directories.tab) - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) -- Create a new tab and verify it has inherited the directory command('tabnew') - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) -- Change tab and change back, verify that directories are correct command('tabnext') eq(globalDir, tcwd()) command('tabprevious') - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) end) end) @@ -173,7 +172,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do -- Change tab-local working directory and verify it is different command('silent t' .. cmd .. ' ' .. directories.tab) - eq(globalDir .. '/' .. directories.tab, cwd()) + eq(globalDir .. pathsep .. directories.tab, cwd()) eq(cwd(), tcwd()) -- working directory maches tab directory eq(1, tlwd()) eq(cwd(), wcwd()) -- still no window-directory @@ -183,16 +182,16 @@ for _, cmd in ipairs {'cd', 'chdir'} do command('new') eq(1, tlwd()) -- Still tab-local working directory eq(0, wlwd()) -- Still no window-local working directory - eq(globalDir .. '/' .. directories.tab, cwd()) + eq(globalDir .. pathsep .. directories.tab, cwd()) command('silent l' .. cmd .. ' ../' .. directories.window) - eq(globalDir .. '/' .. directories.window, cwd()) - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.window, cwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) eq(1, wlwd()) -- Verify the first window still has the tab local directory command('wincmd w') - eq(globalDir .. '/' .. directories.tab, cwd()) - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, cwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) eq(0, wlwd()) -- No window-local directory -- Change back to initial tab and verify working directory has stayed @@ -203,10 +202,10 @@ for _, cmd in ipairs {'cd', 'chdir'} do -- Verify global changes don't affect local ones command('silent ' .. cmd .. ' ' .. directories.global) - eq(globalDir .. '/' .. directories.global, cwd()) + eq(globalDir .. pathsep .. directories.global, cwd()) command('tabnext') - eq(globalDir .. '/' .. directories.tab, cwd()) - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, cwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) eq(0, wlwd()) -- Still no window-local directory in this window -- Unless the global change happened in a tab with local directory @@ -220,9 +219,9 @@ for _, cmd in ipairs {'cd', 'chdir'} do -- But not in a window with its own local directory command('tabnext | wincmd w') - eq(globalDir .. '/' .. directories.window, cwd() ) + eq(globalDir .. pathsep .. directories.window, cwd() ) eq(0 , tlwd()) - eq(globalDir .. '/' .. directories.window, wcwd()) + eq(globalDir .. pathsep .. directories.window, wcwd()) end) end) end @@ -280,6 +279,9 @@ describe("getcwd()", function () end) it("returns empty string if working directory does not exist", function() + if helpers.iswin() then + return + end command("cd "..directories.global) command("call delete('../"..directories.global.."', 'd')") eq("", helpers.eval("getcwd()")) diff --git a/test/functional/ex_cmds/cmd_map_spec.lua b/test/functional/ex_cmds/cmd_map_spec.lua new file mode 100644 index 0000000000..5b7f0942b1 --- /dev/null +++ b/test/functional/ex_cmds/cmd_map_spec.lua @@ -0,0 +1,771 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local feed_command = helpers.feed_command +local feed = helpers.feed +local eq = helpers.eq +local expect = helpers.expect +local eval = helpers.eval +local funcs = helpers.funcs +local insert = helpers.insert +local exc_exec = helpers.exc_exec +local Screen = require('test.functional.ui.screen') + +describe('mappings with <Cmd>', function() + local screen + local function cmdmap(lhs, rhs) + feed_command('noremap '..lhs..' <Cmd>'..rhs..'<cr>') + feed_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] = {background = Screen.colors.LightGrey}, + [6] = {foreground = Screen.colors.Blue1}, + }) + screen:attach() + + cmdmap('<F3>', 'let m = mode(1)') + cmdmap('<F4>', 'normal! ww') + cmdmap('<F5>', 'normal! "ay') + cmdmap('<F6>', 'throw "very error"') + feed_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') + feed_command("abbr foo <Cmd>let g:y = 17<cr>bar") + end) + + it('can be displayed', function() + feed_command('map <F3>') + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {6:<F3>} {6:*} {6:<Cmd>}let m = mode(1){6:<CR>} | + ]]) + end) + + it('handles invalid mappings', function() + feed_command('let x = 0') + feed_command('noremap <F3> <Cmd><Cmd>let x = 1<cr>') + feed('<F3>') + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:E5521: <Cmd> mapping must end with <CR> before second <Cmd>} | + ]]) + + feed_command('noremap <F3> <Cmd><F3>let x = 2<cr>') + feed('<F3>') + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:E5522: <Cmd> mapping must not include <F3> key} | + ]]) + + feed_command('noremap <F3> <Cmd>let x = 3') + feed('<F3>') + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:E5520: <Cmd> mapping must end with <CR>} | + ]]) + eq(0, eval('x')) + 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 + feed_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 + feed_command('tnoremap <F3> <Cmd>let m = mode(1)<cr>') + feed_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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + -- 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() + feed_command('noremap ,x <Cmd>call append(1, "xx")\\| call append(1, "aa")<cr>') + feed_command('noremap ,f <Cmd>nosuchcommand<cr>') + feed_command('noremap ,e <Cmd>throw "very error"\\| call append(1, "yy")<cr>') + feed_command('noremap ,m <Cmd>echoerr "The message."\\| call append(1, "zz")<cr>') + feed_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:~ }| + {1:~ }| + {1:~ }| + | + ]]) + + 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:~ }| + {1:~ }| + {1:~ }| + | + ]]) + + feed_command(':%d') + eq('Vim(echoerr):Err', exc_exec("normal ,w")) + screen:expect([[ + ^ | + 0 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + --No lines in buffer-- | + ]]) + + feed_command(':%d') + feed_command(':normal ,w') + 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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- VISUAL --} | + ]]) + eq('v', funcs.mode(1)) + + -- can invoke operator, ending visual mode + feed('<F5>') + eq('n', funcs.mode(1)) + eq({'some short l'}, funcs.getreg('a',1,1)) + + -- error doesn't interrupt visual mode + feed('ggvw<F6>') + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- VISUAL --} | + ]]) + eq('v', funcs.mode(1)) + feed('<F7>') + screen:expect([[ + so{5:me short lines} | + {5:of }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- VISUAL --} | + ]]) + eq('v', funcs.mode(1)) + + -- startinsert gives "-- (insert) VISUAL --" mode + feed('<F8>') + screen:expect([[ + so{5:me short lines} | + {5:of }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- (insert) VISUAL --} | + ]]) + eq('v', eval('mode(1)')) + feed('<esc>') + eq('i', eval('mode(1)')) + end) + + it('works in select mode', function() + feed_command('snoremap <F1> <cmd>throw "very error"<cr>') + feed_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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- SELECT --} | + ]]) + eq('s', funcs.mode(1)) + + -- visual mapping in select mode restart selct mode after operator + feed('<F5>') + eq('s', funcs.mode(1)) + eq({'some short l'}, funcs.getreg('a',1,1)) + + -- select mode mapping works, and does not restart select mode + feed('<F2>') + eq('n', funcs.mode(1)) + eq({'some short l'}, funcs.getreg('b',1,1)) + + -- error doesn't interrupt temporary visual mode + feed('<esc>ggvw<c-g><F6>') + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- VISUAL --} | + ]]) + -- quirk: restoration of select mode is not performed + eq('v', funcs.mode(1)) + + -- error doesn't interrupt select mode + feed('<esc>ggvw<c-g><F1>') + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- SELECT --} | + ]]) + -- quirk: restoration of select mode is not performed + eq('s', funcs.mode(1)) + + feed('<F7>') + screen:expect([[ + so{5:me short lines} | + {5:of }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- SELECT --} | + ]]) + eq('s', funcs.mode(1)) + + -- startinsert gives "-- SELECT (insert) --" mode + feed('<F8>') + screen:expect([[ + so{5:me short lines} | + {5:of }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {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 '}, funcs.getreg('"',1,1)) + feed('.') + expect([[ + test text]]) + eq({'lines', 'of '}, funcs.getreg('"',1,1)) + feed('uu') + expect([[ + some short lines + of test text]]) + + -- error aborts operator-pending, operator not performed + feed('d<F6>') + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {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(funcs.getreg('b',1,1), {'me short lines', 'of t'}) + + -- 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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT --} | + ]]) + + feed('<F6>') + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT VISUAL --} | + ]]) + expect([[ + indeed some short little lines + of stuff test text]]) + + feed('<F5>') + eq(funcs.getreg('a',1,1), {'deed some short little lines', 'of stuff t'}) + + -- still in insert + screen:expect([[ + in^deed some short little lines | + of stuff test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {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 cmdline mode', function() + cmdmap('<F2>', 'call setcmdpos(2)') + 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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:E492: Not an editor command: text} | + ]]) + + feed(':echo 2<F6>') + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :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([[ + {1:~ }| + {1:~ }| + {1:~ }| + :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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + eq('n', eval('mode(1)')) + + feed(':let g:x = 3<F4>') + screen:expect([[ + some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :let g:x = 3^ | + ]]) + feed('+2<cr>') + -- cursor was moved in the background + screen:expect([[ + some short ^lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :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:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT --} | + ]]) + eq('i', eval('mode(1)')) + eq(9, eval('g:y')) + + end) + +end) + diff --git a/test/functional/ex_cmds/ctrl_c_spec.lua b/test/functional/ex_cmds/ctrl_c_spec.lua index 091a008814..8f76099f79 100644 --- a/test/functional/ex_cmds/ctrl_c_spec.lua +++ b/test/functional/ex_cmds/ctrl_c_spec.lua @@ -47,7 +47,7 @@ describe("CTRL-C (mapped)", function() end -- The test is time-sensitive. Try different sleep values. - local ms_values = {1, 10, 100, 1000, 10000} + local ms_values = {100, 1000, 10000} for i, ms in ipairs(ms_values) do if i < #ms_values then local status, _ = pcall(test_ctrl_c, ms) diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index e3b4a1c504..3d550588e7 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source -local eq, next_msg = helpers.eq, helpers.next_message +local eq, next_msg = helpers.eq, helpers.next_msg local exc_exec = helpers.exc_exec local command = helpers.command local eval = helpers.eval diff --git a/test/functional/ex_cmds/drop_spec.lua b/test/functional/ex_cmds/drop_spec.lua index 9105b84367..30dbd27d37 100644 --- a/test/functional/ex_cmds/drop_spec.lua +++ b/test/functional/ex_cmds/drop_spec.lua @@ -44,14 +44,14 @@ describe(":drop", function() feed_command("edit tmp2") feed_command("drop tmp1") screen:expect([[ - {2:|}^ | - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| + {2:│}^ | + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| {2:tmp2 }{1:tmp1 }| :drop tmp1 | ]]) @@ -64,14 +64,14 @@ describe(":drop", function() feed("iABC<esc>") feed_command("drop tmp3") screen:expect([[ - ^ {2:|} | - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {1:tmp3 }{2:|}{0:~ }| - ABC {2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| + ^ {2:│} | + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {1:tmp3 }{2:│}{0:~ }| + ABC {2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| {2:tmp2 [+] tmp1 }| "tmp3" [New File] | ]]) diff --git a/test/functional/ex_cmds/echo_spec.lua b/test/functional/ex_cmds/echo_spec.lua new file mode 100644 index 0000000000..10c7230896 --- /dev/null +++ b/test/functional/ex_cmds/echo_spec.lua @@ -0,0 +1,321 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local NIL = helpers.NIL +local eval = helpers.eval +local clear = helpers.clear +local meths = helpers.meths +local funcs = helpers.funcs +local source = helpers.source +local dedent = helpers.dedent +local command = helpers.command +local exc_exec = helpers.exc_exec +local redir_exec = helpers.redir_exec + +describe(':echo', function() + before_each(function() + clear() + source([[ + function String(s) + return execute('echo a:s')[1:] + endfunction + ]]) + end) + + describe('used to represent floating-point values', function() + it('dumps NaN values', function() + eq('str2float(\'nan\')', eval('String(str2float(\'nan\'))')) + end) + + it('dumps infinite values', function() + eq('str2float(\'inf\')', eval('String(str2float(\'inf\'))')) + eq('-str2float(\'inf\')', eval('String(str2float(\'-inf\'))')) + end) + + it('dumps regular values', function() + eq('1.5', funcs.String(1.5)) + eq('1.56e-20', funcs.String(1.56000e-020)) + eq('0.0', eval('String(0.0)')) + end) + + it('dumps special v: values', function() + eq('v:true', eval('String(v:true)')) + eq('v:false', eval('String(v:false)')) + eq('v:null', eval('String(v:null)')) + eq('v:true', funcs.String(true)) + eq('v:false', funcs.String(false)) + eq('v:null', funcs.String(NIL)) + end) + + it('dumps values with at most six digits after the decimal point', + function() + eq('1.234568e-20', funcs.String(1.23456789123456789123456789e-020)) + eq('1.234568', funcs.String(1.23456789123456789123456789)) + end) + + it('dumps values with at most seven digits before the decimal point', + function() + eq('1234567.891235', funcs.String(1234567.89123456789123456789)) + eq('1.234568e7', funcs.String(12345678.9123456789123456789)) + end) + + it('dumps negative values', function() + eq('-1.5', funcs.String(-1.5)) + eq('-1.56e-20', funcs.String(-1.56000e-020)) + eq('-1.234568e-20', funcs.String(-1.23456789123456789123456789e-020)) + eq('-1.234568', funcs.String(-1.23456789123456789123456789)) + eq('-1234567.891235', funcs.String(-1234567.89123456789123456789)) + eq('-1.234568e7', funcs.String(-12345678.9123456789123456789)) + end) + end) + + describe('used to represent numbers', function() + it('dumps regular values', function() + eq('0', funcs.String(0)) + eq('-1', funcs.String(-1)) + eq('1', funcs.String(1)) + end) + + it('dumps large values', function() + eq('2147483647', funcs.String(2^31-1)) + eq('-2147483648', funcs.String(-2^31)) + end) + end) + + describe('used to represent strings', function() + it('dumps regular strings', function() + eq('test', funcs.String('test')) + end) + + it('dumps empty strings', function() + eq('', funcs.String('')) + end) + + it('dumps strings with \' inside', function() + eq('\'\'\'', funcs.String('\'\'\'')) + eq('a\'b\'\'', funcs.String('a\'b\'\'')) + eq('\'b\'\'d', funcs.String('\'b\'\'d')) + eq('a\'b\'c\'d', funcs.String('a\'b\'c\'d')) + end) + + it('dumps NULL strings', function() + eq('', eval('String($XXX_UNEXISTENT_VAR_XXX)')) + end) + + it('dumps NULL lists', function() + eq('[]', eval('String(v:_null_list)')) + end) + + it('dumps NULL dictionaries', function() + eq('{}', eval('String(v:_null_dict)')) + end) + end) + + describe('used to represent funcrefs', function() + before_each(function() + source([[ + function Test1() + endfunction + + function s:Test2() dict + endfunction + + function g:Test3() dict + endfunction + + let g:Test2_f = function('s:Test2') + ]]) + end) + + it('dumps references to built-in functions', function() + eq('function', eval('String(function("function"))')) + end) + + it('dumps references to user functions', function() + eq('Test1', eval('String(function("Test1"))')) + eq('g:Test3', eval('String(function("g:Test3"))')) + end) + + it('dumps references to script functions', function() + eq('<SNR>2_Test2', eval('String(Test2_f)')) + end) + + it('dumps partials with self referencing a partial', function() + source([[ + function TestDict() dict + endfunction + let d = {} + let TestDictRef = function('TestDict', d) + let d.tdr = TestDictRef + ]]) + eq(dedent([[ + + function('TestDict', {'tdr': function('TestDict', {...@1})}) + function('TestDict', {'tdr': function('TestDict', {...@1})})]]), + redir_exec('echo String(d.tdr)')) + end) + + it('dumps automatically created partials', function() + eq('function(\'<SNR>2_Test2\', {\'f\': function(\'<SNR>2_Test2\')})', + eval('String({"f": Test2_f}.f)')) + eq('function(\'<SNR>2_Test2\', [1], {\'f\': function(\'<SNR>2_Test2\', [1])})', + eval('String({"f": function(Test2_f, [1])}.f)')) + end) + + it('dumps manually created partials', function() + eq('function(\'Test3\', [1, 2], {})', + eval('String(function("Test3", [1, 2], {}))')) + eq('function(\'Test3\', {})', + eval('String(function("Test3", {}))')) + eq('function(\'Test3\', [1, 2])', + eval('String(function("Test3", [1, 2]))')) + end) + + it('does not crash or halt when dumping partials with reference cycles in self', + function() + meths.set_var('d', {v=true}) + eq(dedent([[ + + {'p': function('<SNR>2_Test2', {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true} + {'p': function('<SNR>2_Test2', {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}]]), + redir_exec('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))')) + end) + + it('does not show errors when dumping partials referencing the same dictionary', + function() + command('let d = {}') + -- Regression for “eval/typval_encode: Dump empty dictionary before + -- checking for refcycle”, results in error. + eq('[function(\'tr\', {}), function(\'tr\', {})]', eval('String([function("tr", d), function("tr", d)])')) + -- Regression for “eval: Work with reference cycles in partials (self) + -- properly”, results in crash. + eval('extend(d, {"a": 1})') + eq('[function(\'tr\', {\'a\': 1}), function(\'tr\', {\'a\': 1})]', eval('String([function("tr", d), function("tr", d)])')) + end) + + it('does not crash or halt when dumping partials with reference cycles in arguments', + function() + meths.set_var('l', {}) + eval('add(l, l)') + -- Regression: the below line used to crash (add returns original list and + -- there was error in dumping partials). Tested explicitly in + -- test/unit/api/private_helpers_spec.lua. + eval('add(l, function("Test1", l))') + eq(dedent([=[ + + function('Test1', [[[...@2], function('Test1', [[...@2]])], function('Test1', [[[...@4], function('Test1', [[...@4]])]])]) + function('Test1', [[[...@2], function('Test1', [[...@2]])], function('Test1', [[[...@4], function('Test1', [[...@4]])]])])]=]), + redir_exec('echo String(function("Test1", l))')) + end) + + it('does not crash or halt when dumping partials with reference cycles in self and arguments', + function() + meths.set_var('d', {v=true}) + meths.set_var('l', {}) + eval('add(l, l)') + eval('add(l, function("Test1", l))') + eval('add(l, function("Test1", d))') + eq(dedent([=[ + + {'p': function('<SNR>2_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true} + {'p': function('<SNR>2_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}]=]), + redir_exec('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))')) + end) + end) + + describe('used to represent lists', function() + it('dumps empty list', function() + eq('[]', funcs.String({})) + end) + + it('dumps nested lists', function() + eq('[[[[[]]]]]', funcs.String({{{{{}}}}})) + end) + + it('dumps nested non-empty lists', function() + eq('[1, [[3, [[5], 4]], 2]]', funcs.String({1, {{3, {{5}, 4}}, 2}})) + end) + + it('does not error when dumping recursive lists', function() + meths.set_var('l', {}) + eval('add(l, l)') + eq(0, exc_exec('echo String(l)')) + end) + + it('dumps recursive lists without error', function() + meths.set_var('l', {}) + eval('add(l, l)') + eq('\n[[...@0]]\n[[...@0]]', redir_exec('echo String(l)')) + eq('\n[[[...@1]]]\n[[[...@1]]]', redir_exec('echo String([l])')) + end) + end) + + describe('used to represent dictionaries', function() + it('dumps empty dictionary', function() + eq('{}', eval('String({})')) + end) + + it('dumps list with two same empty dictionaries, also in partials', function() + command('let d = {}') + eq('[{}, {}]', eval('String([d, d])')) + eq('[function(\'tr\', {}), {}]', eval('String([function("tr", d), d])')) + eq('[{}, function(\'tr\', {})]', eval('String([d, function("tr", d)])')) + end) + + it('dumps non-empty dictionary', function() + eq('{\'t\'\'est\': 1}', funcs.String({['t\'est']=1})) + end) + + it('does not error when dumping recursive dictionaries', function() + meths.set_var('d', {d=1}) + eval('extend(d, {"d": d})') + eq(0, exc_exec('echo String(d)')) + end) + + it('dumps recursive dictionaries without the error', function() + meths.set_var('d', {d=1}) + eval('extend(d, {"d": d})') + eq('\n{\'d\': {...@0}}\n{\'d\': {...@0}}', + redir_exec('echo String(d)')) + eq('\n{\'out\': {\'d\': {...@1}}}\n{\'out\': {\'d\': {...@1}}}', + redir_exec('echo String({"out": d})')) + end) + end) + + describe('used to represent special values', function() + local function chr(n) + return ('%c'):format(n) + end + local function ctrl(c) + return ('%c'):format(c:upper():byte() - 0x40) + end + it('displays hex as hex', function() + -- Regression: due to missing (uint8_t) cast \x80 was represented as + -- ~@<80>. + eq('<80>', funcs.String(chr(0x80))) + eq('<81>', funcs.String(chr(0x81))) + eq('<8e>', funcs.String(chr(0x8e))) + eq('<c2>', funcs.String(('«'):sub(1, 1))) + eq('«', funcs.String(('«'):sub(1, 2))) + end) + it('displays ASCII control characters using ^X notation', function() + eq('^C', funcs.String(ctrl('c'))) + eq('^A', funcs.String(ctrl('a'))) + eq('^F', funcs.String(ctrl('f'))) + end) + it('prints CR, NL and tab as-is', function() + eq('\n', funcs.String('\n')) + eq('\r', funcs.String('\r')) + eq('\t', funcs.String('\t')) + end) + it('prints non-printable UTF-8 in <> notation', function() + -- SINGLE SHIFT TWO, unicode control + eq('<8e>', funcs.String(funcs.nr2char(0x8E))) + -- Surrogate pair: U+1F0A0 PLAYING CARD BACK is represented in UTF-16 as + -- 0xD83C 0xDCA0. This is not valid in UTF-8. + eq('<d83c>', funcs.String(funcs.nr2char(0xD83C))) + eq('<dca0>', funcs.String(funcs.nr2char(0xDCA0))) + eq('<d83c><dca0>', funcs.String(funcs.nr2char(0xD83C) .. funcs.nr2char(0xDCA0))) + end) + end) +end) diff --git a/test/functional/ex_cmds/highlight_spec.lua b/test/functional/ex_cmds/highlight_spec.lua new file mode 100644 index 0000000000..25968b8204 --- /dev/null +++ b/test/functional/ex_cmds/highlight_spec.lua @@ -0,0 +1,43 @@ +local Screen = require('test.functional.ui.screen') +local helpers = require("test.functional.helpers")(after_each) +local eq, command = helpers.eq, helpers.command +local clear = helpers.clear +local eval, exc_exec = helpers.eval, helpers.exc_exec + +describe(':highlight', function() + local screen + + before_each(function() + clear() + screen = Screen.new() + screen:attach() + end) + + after_each(function() + screen:detach() + end) + + it('invalid color name', function() + eq('Vim(highlight):E421: Color name or number not recognized: ctermfg=#181818', + exc_exec("highlight normal ctermfg=#181818")) + eq('Vim(highlight):E421: Color name or number not recognized: ctermbg=#181818', + exc_exec("highlight normal ctermbg=#181818")) + end) + + it('invalid group name', function() + eq('Vim(highlight):E411: highlight group not found: foo', + exc_exec("highlight foo")) + end) + + it('"Normal" foreground with red', function() + eq('', eval('synIDattr(hlID("Normal"), "fg", "cterm")')) + command('highlight normal ctermfg=red') + eq('9', eval('synIDattr(hlID("Normal"), "fg", "cterm")')) + end) + + it('"Normal" background with red', function() + eq('', eval('synIDattr(hlID("Normal"), "bg", "cterm")')) + command('highlight normal ctermbg=red') + eq('9', eval('synIDattr(hlID("Normal"), "bg", "cterm")')) + end) +end) diff --git a/test/functional/ex_cmds/map_spec.lua b/test/functional/ex_cmds/map_spec.lua new file mode 100644 index 0000000000..84d5bc2335 --- /dev/null +++ b/test/functional/ex_cmds/map_spec.lua @@ -0,0 +1,28 @@ +local helpers = require("test.functional.helpers")(after_each) + +local eq = helpers.eq +local feed = helpers.feed +local meths = helpers.meths +local clear = helpers.clear +local command = helpers.command +local expect = helpers.expect + +describe(':*map', function() + before_each(clear) + + it('are not affected by &isident', function() + meths.set_var('counter', 0) + command('nnoremap <C-x> :let counter+=1<CR>') + meths.set_option('isident', ('%u'):format(('>'):byte())) + command('nnoremap <C-y> :let counter+=1<CR>') + -- &isident used to disable keycode parsing here as well + feed('\24\25<C-x><C-y>') + eq(4, meths.get_var('counter')) + end) + + it(':imap <M-">', function() + command('imap <M-"> foo') + feed('i-<M-">-') + expect('-foo-') + end) +end) diff --git a/test/functional/ex_cmds/menu_spec.lua b/test/functional/ex_cmds/menu_spec.lua index 57198600b9..2c0535acda 100644 --- a/test/functional/ex_cmds/menu_spec.lua +++ b/test/functional/ex_cmds/menu_spec.lua @@ -2,6 +2,8 @@ local helpers = require('test.functional.helpers')(after_each) local clear, command, nvim = helpers.clear, helpers.command, helpers.nvim local expect, feed = helpers.expect, helpers.feed local eq, eval = helpers.eq, helpers.eval +local funcs = helpers.funcs + describe(':emenu', function() @@ -56,3 +58,572 @@ describe(':emenu', function() eq('thiscmdmode', eval('getcmdline()')) end) end) + +describe('menu_get', function() + + before_each(function() + clear() + command('nnoremenu &Test.Test inormal<ESC>') + command('inoremenu Test.Test insert') + command('vnoremenu Test.Test x') + command('cnoremenu Test.Test cmdmode') + command('menu Test.Nested.test level1') + command('menu Test.Nested.Nested2 level2') + + command('nnoremenu <script> Export.Script p') + command('tmenu Export.Script This is the tooltip') + command('menu ]Export.hidden thisoneshouldbehidden') + + command('nnoremenu Edit.Paste p') + command('cnoremenu Edit.Paste <C-R>"') + end) + + it("path='', modes='a'", function() + local m = funcs.menu_get("","a"); + -- HINT: To print the expected table and regenerate the tests: + -- print(require('pl.pretty').dump(m)) + local expected = { + { + shortcut = "T", + hidden = 0, + submenus = { + { + mappings = { + i = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "insert", + silent = 0 + }, + s = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "x", + silent = 0 + }, + n = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "inormal<Esc>", + silent = 0 + }, + v = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "x", + silent = 0 + }, + c = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "cmdmode", + silent = 0 + } + }, + priority = 500, + name = "Test", + hidden = 0 + }, + { + priority = 500, + name = "Nested", + submenus = { + { + mappings = { + o = { + sid = 0, + noremap = 0, + enabled = 1, + rhs = "level1", + silent = 0 + }, + v = { + sid = 0, + noremap = 0, + enabled = 1, + rhs = "level1", + silent = 0 + }, + s = { + sid = 0, + noremap = 0, + enabled = 1, + rhs = "level1", + silent = 0 + }, + n = { + sid = 0, + noremap = 0, + enabled = 1, + rhs = "level1", + silent = 0 + } + }, + priority = 500, + name = "test", + hidden = 0 + }, + { + mappings = { + o = { + sid = 0, + noremap = 0, + enabled = 1, + rhs = "level2", + silent = 0 + }, + v = { + sid = 0, + noremap = 0, + enabled = 1, + rhs = "level2", + silent = 0 + }, + s = { + sid = 0, + noremap = 0, + enabled = 1, + rhs = "level2", + silent = 0 + }, + n = { + sid = 0, + noremap = 0, + enabled = 1, + rhs = "level2", + silent = 0 + } + }, + priority = 500, + name = "Nested2", + hidden = 0 + } + }, + hidden = 0 + } + }, + priority = 500, + name = "Test" + }, + { + priority = 500, + name = "Export", + submenus = { + { + tooltip = "This is the tooltip", + hidden = 0, + name = "Script", + priority = 500, + mappings = { + n = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "p", + silent = 0 + } + } + } + }, + hidden = 0 + }, + { + priority = 500, + name = "Edit", + submenus = { + { + mappings = { + c = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "<C-R>\"", + silent = 0 + }, + n = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "p", + silent = 0 + } + }, + priority = 500, + name = "Paste", + hidden = 0 + } + }, + hidden = 0 + }, + { + priority = 500, + name = "]Export", + submenus = { + { + mappings = { + o = { + sid = 0, + noremap = 0, + enabled = 1, + rhs = "thisoneshouldbehidden", + silent = 0 + }, + v = { + sid = 0, + noremap = 0, + enabled = 1, + rhs = "thisoneshouldbehidden", + silent = 0 + }, + s = { + sid = 0, + noremap = 0, + enabled = 1, + rhs = "thisoneshouldbehidden", + silent = 0 + }, + n = { + sid = 0, + noremap = 0, + enabled = 1, + rhs = "thisoneshouldbehidden", + silent = 0 + } + }, + priority = 500, + name = "hidden", + hidden = 0 + } + }, + hidden = 1 + } + } + eq(expected, m) + end) + + it('matching path, default modes', function() + local m = funcs.menu_get("Export", "a") + local expected = { + { + tooltip = "This is the tooltip", + hidden = 0, + name = "Script", + priority = 500, + mappings = { + n = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "p", + silent = 0 + } + } + } + } + eq(expected, m) + end) + + it('no path, matching modes', function() + local m = funcs.menu_get("","i") + local expected = { + { + shortcut = "T", + hidden = 0, + submenus = { + { + mappings = { + i = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "insert", + silent = 0 + } + }, + priority = 500, + name = "Test", + hidden = 0 + }, + { + } + }, + priority = 500, + name = "Test" + } + } + eq(expected, m) + end) + + it('matching path and modes', function() + local m = funcs.menu_get("Test","i") + local expected = { + { + mappings = { + i = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "insert", + silent = 0 + } + }, + priority = 500, + name = "Test", + hidden = 0 + } + } + eq(expected, m) + end) +end) + +describe('menu_get', function() + + before_each(function() + clear() + end) + + it('returns <keycode> representation of special keys', function() + command('nnoremenu &Test.Test inormal<ESC>') + command('inoremenu &Test.Test2 <Tab><Esc>') + command('vnoremenu &Test.Test3 yA<C-R>0<Tab>xyz<Esc>') + command('inoremenu &Test.Test4 <c-r>*') + command('inoremenu &Test.Test5 <c-R>+') + command('nnoremenu &Test.Test6 <Nop>') + command('nnoremenu &Test.Test7 <NOP>') + command('nnoremenu &Test.Test8 <NoP>') + command('nnoremenu &Test.Test9 ""') + + local m = funcs.menu_get(""); + local expected = { + { + shortcut = "T", + hidden = 0, + submenus = { + { + priority = 500, + mappings = { + n = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "inormal<Esc>", + silent = 0 + } + }, + name = "Test", + hidden = 0 + }, + { + priority = 500, + mappings = { + i = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "<Tab><Esc>", + silent = 0 + } + }, + name = "Test2", + hidden = 0 + }, + { + priority = 500, + mappings = { + s = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "yA<C-R>0<Tab>xyz<Esc>", + silent = 0 + }, + v = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "yA<C-R>0<Tab>xyz<Esc>", + silent = 0 + } + }, + name = "Test3", + hidden = 0 + }, + { + priority = 500, + mappings = { + i = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "<C-R>*", + silent = 0 + } + }, + name = "Test4", + hidden = 0 + }, + { + priority = 500, + mappings = { + i = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "<C-R>+", + silent = 0 + } + }, + name = "Test5", + hidden = 0 + }, + { + priority = 500, + mappings = { + n = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "", + silent = 0 + } + }, + name = "Test6", + hidden = 0 + }, + { + priority = 500, + mappings = { + n = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "", + silent = 0 + } + }, + name = "Test7", + hidden = 0 + }, + { + priority = 500, + mappings = { + n = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "", + silent = 0 + } + }, + name = "Test8", + hidden = 0 + }, + { + priority = 500, + mappings = { + n = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "\"\"", + silent = 0 + } + }, + name = "Test9", + hidden = 0 + } + }, + priority = 500, + name = "Test" + } + } + + eq(m, expected) + end) + + it('works with right-aligned text and spaces', function() + command('nnoremenu &Test<Tab>Y.Test<Tab>X\\ x inormal<Alt-j>') + command('nnoremenu &Test\\ 1.Test\\ 2 Wargl') + command('nnoremenu &Test4.Test<Tab>3 i space<Esc>') + + local m = funcs.menu_get(""); + local expected = { + { + shortcut = "T", + hidden = 0, + actext = "Y", + submenus = { + { + mappings = { + n = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "inormal<Alt-j>", + silent = 0 + } + }, + hidden = 0, + actext = "X x", + priority = 500, + name = "Test" + } + }, + priority = 500, + name = "Test" + }, + { + shortcut = "T", + hidden = 0, + submenus = { + { + priority = 500, + mappings = { + n = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "Wargl", + silent = 0 + } + }, + name = "Test 2", + hidden = 0 + } + }, + priority = 500, + name = "Test 1" + }, + { + shortcut = "T", + hidden = 0, + submenus = { + { + mappings = { + n = { + sid = 1, + noremap = 1, + enabled = 1, + rhs = "i space<Esc>", + silent = 0 + } + }, + hidden = 0, + actext = "3", + priority = 500, + name = "Test" + } + }, + priority = 500, + name = "Test4" + } + } + + eq(m, expected) + end) +end) diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua index 5d658f10bb..a5b327095e 100644 --- a/test/functional/ex_cmds/mksession_spec.lua +++ b/test/functional/ex_cmds/mksession_spec.lua @@ -6,6 +6,7 @@ local command = helpers.command local get_pathsep = helpers.get_pathsep local eq = helpers.eq local funcs = helpers.funcs +local rmdir = helpers.rmdir local file_prefix = 'Xtest-functional-ex_cmds-mksession_spec' @@ -20,7 +21,7 @@ describe(':mksession', function() after_each(function() os.remove(session_file) - lfs.rmdir(tab_dir) + rmdir(tab_dir) end) it('restores tab-local working directories', function() diff --git a/test/functional/ex_cmds/mkview_spec.lua b/test/functional/ex_cmds/mkview_spec.lua new file mode 100644 index 0000000000..fef8065b2e --- /dev/null +++ b/test/functional/ex_cmds/mkview_spec.lua @@ -0,0 +1,67 @@ +local lfs = require('lfs') +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local command = helpers.command +local get_pathsep = helpers.get_pathsep +local eq = helpers.eq +local funcs = helpers.funcs +local rmdir = helpers.rmdir + +local file_prefix = 'Xtest-functional-ex_cmds-mkview_spec' + +describe(':mkview', function() + local tmp_file_base = file_prefix .. '-tmpfile' + local local_dir = file_prefix .. '.d' + local view_dir = file_prefix .. '.view.d' + + before_each(function() + clear() + lfs.mkdir(view_dir) + lfs.mkdir(local_dir) + end) + + after_each(function() + -- Remove any views created in the view directory + rmdir(view_dir) + rmdir(local_dir) + end) + + it('viewoption curdir restores local current directory', function() + local cwd_dir = funcs.getcwd() + local set_view_dir_command = 'set viewdir=' .. cwd_dir .. + get_pathsep() .. view_dir + + -- By default the local current directory should save + command(set_view_dir_command) + command('edit ' .. tmp_file_base .. '1') + command('lcd ' .. local_dir) + command('mkview') + + -- Create a new instance of Nvim to remove the 'lcd' + clear() + + -- Disable saving the local current directory for the second view + command(set_view_dir_command) + command('set viewoptions-=curdir') + command('edit ' .. tmp_file_base .. '2') + command('lcd ' .. local_dir) + command('mkview') + + -- Create a new instance of Nvim to test saved 'lcd' option + clear() + command(set_view_dir_command) + + -- Load the view without a saved local current directory + command('edit ' .. tmp_file_base .. '2') + command('loadview') + -- The view's current directory should not have changed + eq(cwd_dir, funcs.getcwd()) + -- Load the view with a saved local current directory + command('edit ' .. tmp_file_base .. '1') + command('loadview') + -- The view's local directory should have been saved + eq(cwd_dir .. get_pathsep() .. local_dir, funcs.getcwd()) + end) + +end) diff --git a/test/functional/ex_cmds/quickfix_commands_spec.lua b/test/functional/ex_cmds/quickfix_commands_spec.lua index 5ab34db3fb..bf10f80401 100644 --- a/test/functional/ex_cmds/quickfix_commands_spec.lua +++ b/test/functional/ex_cmds/quickfix_commands_spec.lua @@ -7,6 +7,7 @@ local command = helpers.command local exc_exec = helpers.exc_exec local write_file = helpers.write_file local curbufmeths = helpers.curbufmeths +local source = helpers.source local file_base = 'Xtest-functional-ex_cmds-quickfix_commands' @@ -81,3 +82,30 @@ for _, c in ipairs({'l', 'c'}) do end) end) end + +describe('quickfix', function() + it('location-list update on buffer modification', function() + source([[ + new + setl bt=nofile + let lines = ['Line 1', 'Line 2', 'Line 3', 'Line 4', 'Line 5'] + call append(0, lines) + new + setl bt=nofile + call append(0, lines) + let qf_item = { + \ 'lnum': 4, + \ 'text': "This is the error line.", + \ } + let qf_item['bufnr'] = bufnr('%') + call setloclist(0, [qf_item]) + wincmd p + let qf_item['bufnr'] = bufnr('%') + call setloclist(0, [qf_item]) + 1del _ + call append(0, ['New line 1', 'New line 2', 'New line 3']) + silent ll + ]]) + eq({0, 6, 1, 0, 1}, funcs.getcurpos()) + end) +end) diff --git a/test/functional/ex_cmds/sign_spec.lua b/test/functional/ex_cmds/sign_spec.lua index b37e6e8563..df0f5db860 100644 --- a/test/functional/ex_cmds/sign_spec.lua +++ b/test/functional/ex_cmds/sign_spec.lua @@ -16,8 +16,8 @@ describe('sign', function() nvim('command', 'sign place 34 line=3 name=Foo buffer='..buf2) -- now unplace without specifying a buffer nvim('command', 'sign unplace 34') - eq("\n--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf1)) - eq("\n--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf2)) + eq("--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf1)) + eq("--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf2)) end) end) end) diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua index 863d439080..bcf83698bb 100644 --- a/test/functional/ex_cmds/write_spec.lua +++ b/test/functional/ex_cmds/write_spec.lua @@ -10,8 +10,6 @@ local feed_command = helpers.feed_command local funcs = helpers.funcs local meths = helpers.meths -if helpers.pending_win32(pending) then return end - local fname = 'Xtest-functional-ex_cmds-write' local fname_bak = fname .. '~' local fname_broken = fname_bak .. 'broken' @@ -36,7 +34,11 @@ describe(':write', function() it('&backupcopy=auto preserves symlinks', function() command('set backupcopy=auto') write_file('test_bkc_file.txt', 'content0') - command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + if helpers.iswin() then + command("silent !mklink test_bkc_link.txt test_bkc_file.txt") + else + command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + end source([[ edit test_bkc_link.txt call setline(1, ['content1']) @@ -49,7 +51,11 @@ describe(':write', function() it('&backupcopy=no replaces symlink with new file', function() command('set backupcopy=no') write_file('test_bkc_file.txt', 'content0') - command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + if helpers.iswin() then + command("silent !mklink test_bkc_link.txt test_bkc_file.txt") + else + command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + end source([[ edit test_bkc_link.txt call setline(1, ['content1']) @@ -82,8 +88,10 @@ describe(':write', function() command('let $HOME=""') eq(funcs.fnamemodify('.', ':p:h'), funcs.fnamemodify('.', ':p:h:~')) -- Message from check_overwrite - eq(('\nE17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'), - redir_exec('write .')) + if not helpers.iswin() then + eq(('\nE17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'), + redir_exec('write .')) + end meths.set_option('writeany', true) -- Message from buf_write eq(('\nE502: "." is a directory'), @@ -100,9 +108,16 @@ describe(':write', function() funcs.setfperm(fname, 'r--------') eq('Vim(write):E505: "Xtest-functional-ex_cmds-write" is read-only (add ! to override)', exc_exec('write')) - os.remove(fname) - os.remove(fname_bak) + if helpers.iswin() then + eq(0, os.execute('del /q/f ' .. fname)) + eq(0, os.execute('rd /q/s ' .. fname_bak)) + else + eq(true, os.remove(fname)) + eq(true, os.remove(fname_bak)) + end write_file(fname_bak, 'TTYX') + -- FIXME: exc_exec('write!') outputs 0 in Windows + if helpers.iswin() then return end lfs.link(fname_bak .. ('/xxxxx'):rep(20), fname, true) eq('Vim(write):E166: Can\'t open linked file for writing', exc_exec('write!')) |