From d8de4eb685e35646c7d541e9a75bdc296127b7e2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 17 Sep 2021 09:16:40 -0700 Subject: test: reorg #15698 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Subdirectories like "visual", "insert", "normal" encourage people to separate *related* tests for no good reason. Typically the _mode_ is not the relevant topic of a test (and when it is, _then_ create an appropriate describe() or it()). Solution: - Delete the various `test/functional//` subdirectories, move their tests to more meaningful topics. - Rename `…/normal/` to `…/editor/`. - Move or merge `…/visual/*` and `…/insert/*` tests into here where appropriate. - Rename `…/eval/` to `…/vimscript/`. - Move `…/viml/*` into here also. * test(reorg): insert/* => editor/mode_insert_spec.lua * test(reorg): cmdline/* => editor/mode_cmdline_spec.lua * test(reorg): eval core tests => eval_spec.lua --- test/functional/autocmd/focus_spec.lua | 63 ++ test/functional/autoread/focus_spec.lua | 63 -- test/functional/cmdline/ctrl_r_spec.lua | 34 - test/functional/cmdline/history_spec.lua | 36 - test/functional/editor/K_spec.lua | 38 + test/functional/editor/completion_spec.lua | 1196 ++++++++++++++++++++ test/functional/editor/count_spec.lua | 39 + test/functional/editor/fold_spec.lua | 362 ++++++ test/functional/editor/jump_spec.lua | 139 +++ test/functional/editor/lang_spec.lua | 63 ++ test/functional/editor/langmap_spec.lua | 280 +++++ test/functional/editor/macro_spec.lua | 30 + test/functional/editor/meta_key_spec.lua | 55 + test/functional/editor/mode_cmdline_spec.lua | 69 ++ test/functional/editor/mode_insert_spec.lua | 89 ++ test/functional/editor/mode_visual_spec.lua | 26 + test/functional/editor/put_spec.lua | 939 +++++++++++++++ test/functional/editor/search_spec.lua | 17 + test/functional/editor/tabpage_spec.lua | 38 + test/functional/editor/undo_spec.lua | 61 + test/functional/eval/api_functions_spec.lua | 167 --- test/functional/eval/backtick_expansion_spec.lua | 50 - test/functional/eval/buf_functions_spec.lua | 306 ----- test/functional/eval/changedtick_spec.lua | 142 --- test/functional/eval/container_functions_spec.lua | 24 - test/functional/eval/ctx_functions_spec.lua | 406 ------- test/functional/eval/environ_spec.lua | 80 -- test/functional/eval/executable_spec.lua | 218 ---- test/functional/eval/execute_spec.lua | 337 ------ test/functional/eval/exepath_spec.lua | 40 - test/functional/eval/fnamemodify_spec.lua | 156 --- test/functional/eval/function_spec.lua | 37 - test/functional/eval/getline_spec.lua | 39 - test/functional/eval/glob_spec.lua | 28 - test/functional/eval/has_spec.lua | 66 -- test/functional/eval/hostname_spec.lua | 20 - test/functional/eval/input_spec.lua | 483 -------- test/functional/eval/interrupt_spec.lua | 61 - test/functional/eval/json_functions_spec.lua | 795 ------------- test/functional/eval/let_spec.lua | 93 -- test/functional/eval/map_functions_spec.lua | 163 --- test/functional/eval/match_functions_spec.lua | 157 --- test/functional/eval/minmax_functions_spec.lua | 51 - test/functional/eval/modeline_spec.lua | 19 - test/functional/eval/msgpack_functions_spec.lua | 755 ------------ test/functional/eval/null_spec.lua | 170 --- test/functional/eval/operators_spec.lua | 28 - test/functional/eval/printf_spec.lua | 92 -- test/functional/eval/reltime_spec.lua | 53 - test/functional/eval/server_spec.lua | 156 --- test/functional/eval/setpos_spec.lua | 64 -- test/functional/eval/sort_spec.lua | 57 - test/functional/eval/special_vars_spec.lua | 190 ---- test/functional/eval/string_spec.lua | 277 ----- test/functional/eval/system_spec.lua | 589 ---------- test/functional/eval/timer_spec.lua | 265 ----- test/functional/eval/uniq_spec.lua | 31 - test/functional/eval/vvar_event_spec.lua | 15 - test/functional/eval/wait_spec.lua | 78 -- test/functional/eval/writefile_spec.lua | 156 --- test/functional/insert/ctrl_o_spec.lua | 54 - test/functional/insert/ctrl_r_spec.lua | 19 - test/functional/insert/insert_spec.lua | 47 - test/functional/normal/K_spec.lua | 38 - test/functional/normal/count_spec.lua | 39 - test/functional/normal/fold_spec.lua | 362 ------ test/functional/normal/jump_spec.lua | 139 --- test/functional/normal/lang_spec.lua | 63 -- test/functional/normal/langmap_spec.lua | 280 ----- test/functional/normal/macro_spec.lua | 30 - test/functional/normal/meta_key_spec.lua | 22 - test/functional/normal/put_spec.lua | 939 --------------- test/functional/normal/search_spec.lua | 17 - test/functional/normal/tabpage_spec.lua | 38 - test/functional/normal/undo_spec.lua | 61 - test/functional/viml/completion_spec.lua | 1196 -------------------- test/functional/viml/errorlist_spec.lua | 84 -- test/functional/viml/function_spec.lua | 216 ---- test/functional/viml/lang_spec.lua | 30 - test/functional/vimscript/api_functions_spec.lua | 167 +++ test/functional/vimscript/buf_functions_spec.lua | 306 +++++ test/functional/vimscript/changedtick_spec.lua | 142 +++ .../vimscript/container_functions_spec.lua | 24 + test/functional/vimscript/ctx_functions_spec.lua | 406 +++++++ test/functional/vimscript/environ_spec.lua | 80 ++ test/functional/vimscript/errorlist_spec.lua | 84 ++ test/functional/vimscript/eval_spec.lua | 146 +++ test/functional/vimscript/executable_spec.lua | 218 ++++ test/functional/vimscript/execute_spec.lua | 337 ++++++ test/functional/vimscript/exepath_spec.lua | 40 + test/functional/vimscript/fnamemodify_spec.lua | 156 +++ test/functional/vimscript/functions_spec.lua | 20 + test/functional/vimscript/getline_spec.lua | 39 + test/functional/vimscript/glob_spec.lua | 28 + test/functional/vimscript/has_spec.lua | 66 ++ test/functional/vimscript/hostname_spec.lua | 20 + test/functional/vimscript/input_spec.lua | 483 ++++++++ test/functional/vimscript/json_functions_spec.lua | 795 +++++++++++++ test/functional/vimscript/lang_spec.lua | 30 + test/functional/vimscript/let_spec.lua | 93 ++ test/functional/vimscript/map_functions_spec.lua | 163 +++ test/functional/vimscript/match_functions_spec.lua | 157 +++ .../functional/vimscript/minmax_functions_spec.lua | 51 + test/functional/vimscript/modeline_spec.lua | 19 + .../vimscript/msgpack_functions_spec.lua | 755 ++++++++++++ test/functional/vimscript/null_spec.lua | 170 +++ test/functional/vimscript/operators_spec.lua | 28 + test/functional/vimscript/printf_spec.lua | 92 ++ test/functional/vimscript/reltime_spec.lua | 53 + test/functional/vimscript/server_spec.lua | 156 +++ test/functional/vimscript/setpos_spec.lua | 64 ++ test/functional/vimscript/sort_spec.lua | 57 + test/functional/vimscript/special_vars_spec.lua | 190 ++++ test/functional/vimscript/string_spec.lua | 277 +++++ test/functional/vimscript/system_spec.lua | 589 ++++++++++ test/functional/vimscript/timer_spec.lua | 265 +++++ test/functional/vimscript/uniq_spec.lua | 31 + test/functional/vimscript/vvar_event_spec.lua | 15 + test/functional/vimscript/wait_spec.lua | 78 ++ test/functional/vimscript/writefile_spec.lua | 156 +++ test/functional/visual/ctrl_o_spec.lua | 23 - test/functional/visual/meta_key_spec.lua | 22 - 122 files changed, 10550 insertions(+), 10766 deletions(-) create mode 100644 test/functional/autocmd/focus_spec.lua delete mode 100644 test/functional/autoread/focus_spec.lua delete mode 100644 test/functional/cmdline/ctrl_r_spec.lua delete mode 100644 test/functional/cmdline/history_spec.lua create mode 100644 test/functional/editor/K_spec.lua create mode 100644 test/functional/editor/completion_spec.lua create mode 100644 test/functional/editor/count_spec.lua create mode 100644 test/functional/editor/fold_spec.lua create mode 100644 test/functional/editor/jump_spec.lua create mode 100644 test/functional/editor/lang_spec.lua create mode 100644 test/functional/editor/langmap_spec.lua create mode 100644 test/functional/editor/macro_spec.lua create mode 100644 test/functional/editor/meta_key_spec.lua create mode 100644 test/functional/editor/mode_cmdline_spec.lua create mode 100644 test/functional/editor/mode_insert_spec.lua create mode 100644 test/functional/editor/mode_visual_spec.lua create mode 100644 test/functional/editor/put_spec.lua create mode 100644 test/functional/editor/search_spec.lua create mode 100644 test/functional/editor/tabpage_spec.lua create mode 100644 test/functional/editor/undo_spec.lua delete mode 100644 test/functional/eval/api_functions_spec.lua delete mode 100644 test/functional/eval/backtick_expansion_spec.lua delete mode 100644 test/functional/eval/buf_functions_spec.lua delete mode 100644 test/functional/eval/changedtick_spec.lua delete mode 100644 test/functional/eval/container_functions_spec.lua delete mode 100644 test/functional/eval/ctx_functions_spec.lua delete mode 100644 test/functional/eval/environ_spec.lua delete mode 100644 test/functional/eval/executable_spec.lua delete mode 100644 test/functional/eval/execute_spec.lua delete mode 100644 test/functional/eval/exepath_spec.lua delete mode 100644 test/functional/eval/fnamemodify_spec.lua delete mode 100644 test/functional/eval/function_spec.lua delete mode 100644 test/functional/eval/getline_spec.lua delete mode 100644 test/functional/eval/glob_spec.lua delete mode 100644 test/functional/eval/has_spec.lua delete mode 100644 test/functional/eval/hostname_spec.lua delete mode 100644 test/functional/eval/input_spec.lua delete mode 100644 test/functional/eval/interrupt_spec.lua delete mode 100644 test/functional/eval/json_functions_spec.lua delete mode 100644 test/functional/eval/let_spec.lua delete mode 100644 test/functional/eval/map_functions_spec.lua delete mode 100644 test/functional/eval/match_functions_spec.lua delete mode 100644 test/functional/eval/minmax_functions_spec.lua delete mode 100644 test/functional/eval/modeline_spec.lua delete mode 100644 test/functional/eval/msgpack_functions_spec.lua delete mode 100644 test/functional/eval/null_spec.lua delete mode 100644 test/functional/eval/operators_spec.lua delete mode 100644 test/functional/eval/printf_spec.lua delete mode 100644 test/functional/eval/reltime_spec.lua delete mode 100644 test/functional/eval/server_spec.lua delete mode 100644 test/functional/eval/setpos_spec.lua delete mode 100644 test/functional/eval/sort_spec.lua delete mode 100644 test/functional/eval/special_vars_spec.lua delete mode 100644 test/functional/eval/string_spec.lua delete mode 100644 test/functional/eval/system_spec.lua delete mode 100644 test/functional/eval/timer_spec.lua delete mode 100644 test/functional/eval/uniq_spec.lua delete mode 100644 test/functional/eval/vvar_event_spec.lua delete mode 100644 test/functional/eval/wait_spec.lua delete mode 100644 test/functional/eval/writefile_spec.lua delete mode 100644 test/functional/insert/ctrl_o_spec.lua delete mode 100644 test/functional/insert/ctrl_r_spec.lua delete mode 100644 test/functional/insert/insert_spec.lua delete mode 100644 test/functional/normal/K_spec.lua delete mode 100644 test/functional/normal/count_spec.lua delete mode 100644 test/functional/normal/fold_spec.lua delete mode 100644 test/functional/normal/jump_spec.lua delete mode 100644 test/functional/normal/lang_spec.lua delete mode 100644 test/functional/normal/langmap_spec.lua delete mode 100644 test/functional/normal/macro_spec.lua delete mode 100644 test/functional/normal/meta_key_spec.lua delete mode 100644 test/functional/normal/put_spec.lua delete mode 100644 test/functional/normal/search_spec.lua delete mode 100644 test/functional/normal/tabpage_spec.lua delete mode 100644 test/functional/normal/undo_spec.lua delete mode 100644 test/functional/viml/completion_spec.lua delete mode 100644 test/functional/viml/errorlist_spec.lua delete mode 100644 test/functional/viml/function_spec.lua delete mode 100644 test/functional/viml/lang_spec.lua create mode 100644 test/functional/vimscript/api_functions_spec.lua create mode 100644 test/functional/vimscript/buf_functions_spec.lua create mode 100644 test/functional/vimscript/changedtick_spec.lua create mode 100644 test/functional/vimscript/container_functions_spec.lua create mode 100644 test/functional/vimscript/ctx_functions_spec.lua create mode 100644 test/functional/vimscript/environ_spec.lua create mode 100644 test/functional/vimscript/errorlist_spec.lua create mode 100644 test/functional/vimscript/eval_spec.lua create mode 100644 test/functional/vimscript/executable_spec.lua create mode 100644 test/functional/vimscript/execute_spec.lua create mode 100644 test/functional/vimscript/exepath_spec.lua create mode 100644 test/functional/vimscript/fnamemodify_spec.lua create mode 100644 test/functional/vimscript/functions_spec.lua create mode 100644 test/functional/vimscript/getline_spec.lua create mode 100644 test/functional/vimscript/glob_spec.lua create mode 100644 test/functional/vimscript/has_spec.lua create mode 100644 test/functional/vimscript/hostname_spec.lua create mode 100644 test/functional/vimscript/input_spec.lua create mode 100644 test/functional/vimscript/json_functions_spec.lua create mode 100644 test/functional/vimscript/lang_spec.lua create mode 100644 test/functional/vimscript/let_spec.lua create mode 100644 test/functional/vimscript/map_functions_spec.lua create mode 100644 test/functional/vimscript/match_functions_spec.lua create mode 100644 test/functional/vimscript/minmax_functions_spec.lua create mode 100644 test/functional/vimscript/modeline_spec.lua create mode 100644 test/functional/vimscript/msgpack_functions_spec.lua create mode 100644 test/functional/vimscript/null_spec.lua create mode 100644 test/functional/vimscript/operators_spec.lua create mode 100644 test/functional/vimscript/printf_spec.lua create mode 100644 test/functional/vimscript/reltime_spec.lua create mode 100644 test/functional/vimscript/server_spec.lua create mode 100644 test/functional/vimscript/setpos_spec.lua create mode 100644 test/functional/vimscript/sort_spec.lua create mode 100644 test/functional/vimscript/special_vars_spec.lua create mode 100644 test/functional/vimscript/string_spec.lua create mode 100644 test/functional/vimscript/system_spec.lua create mode 100644 test/functional/vimscript/timer_spec.lua create mode 100644 test/functional/vimscript/uniq_spec.lua create mode 100644 test/functional/vimscript/vvar_event_spec.lua create mode 100644 test/functional/vimscript/wait_spec.lua create mode 100644 test/functional/vimscript/writefile_spec.lua delete mode 100644 test/functional/visual/ctrl_o_spec.lua delete mode 100644 test/functional/visual/meta_key_spec.lua diff --git a/test/functional/autocmd/focus_spec.lua b/test/functional/autocmd/focus_spec.lua new file mode 100644 index 0000000000..3f9a0ad09b --- /dev/null +++ b/test/functional/autocmd/focus_spec.lua @@ -0,0 +1,63 @@ +local helpers = require('test.functional.helpers')(after_each) +local thelpers = require('test.functional.terminal.helpers') +local lfs = require('lfs') +local clear = helpers.clear +local nvim_prog = helpers.nvim_prog +local feed_command = helpers.feed_command +local feed_data = thelpers.feed_data + +if helpers.pending_win32(pending) then return end + +describe('autoread TUI FocusGained/FocusLost', function() + local f1 = 'xtest-foo' + local screen + + before_each(function() + clear() + screen = thelpers.screen_setup(0, '["'..nvim_prog + ..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler"]') + end) + + teardown(function() + os.remove(f1) + end) + + it('external file change', function() + local path = f1 + local expected_addition = [[ + line 1 + line 2 + line 3 + line 4 + ]] + + helpers.write_file(path, '') + lfs.touch(path, os.time() - 10) + feed_command('edit '..path) + feed_data('\027[O') + + screen:expect{grid=[[ + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:xtest-foo }| + :edit xtest-foo | + {3:-- TERMINAL --} | + ]]} + + helpers.write_file(path, expected_addition) + + feed_data('\027[I') + + screen:expect{grid=[[ + {1:l}ine 1 | + line 2 | + line 3 | + line 4 | + {5:xtest-foo }| + "xtest-foo" 4L, 28C | + {3:-- TERMINAL --} | + ]]} + end) +end) diff --git a/test/functional/autoread/focus_spec.lua b/test/functional/autoread/focus_spec.lua deleted file mode 100644 index 3f9a0ad09b..0000000000 --- a/test/functional/autoread/focus_spec.lua +++ /dev/null @@ -1,63 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local thelpers = require('test.functional.terminal.helpers') -local lfs = require('lfs') -local clear = helpers.clear -local nvim_prog = helpers.nvim_prog -local feed_command = helpers.feed_command -local feed_data = thelpers.feed_data - -if helpers.pending_win32(pending) then return end - -describe('autoread TUI FocusGained/FocusLost', function() - local f1 = 'xtest-foo' - local screen - - before_each(function() - clear() - screen = thelpers.screen_setup(0, '["'..nvim_prog - ..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler"]') - end) - - teardown(function() - os.remove(f1) - end) - - it('external file change', function() - local path = f1 - local expected_addition = [[ - line 1 - line 2 - line 3 - line 4 - ]] - - helpers.write_file(path, '') - lfs.touch(path, os.time() - 10) - feed_command('edit '..path) - feed_data('\027[O') - - screen:expect{grid=[[ - {1: } | - {4:~ }| - {4:~ }| - {4:~ }| - {5:xtest-foo }| - :edit xtest-foo | - {3:-- TERMINAL --} | - ]]} - - helpers.write_file(path, expected_addition) - - feed_data('\027[I') - - screen:expect{grid=[[ - {1:l}ine 1 | - line 2 | - line 3 | - line 4 | - {5:xtest-foo }| - "xtest-foo" 4L, 28C | - {3:-- TERMINAL --} | - ]]} - end) -end) diff --git a/test/functional/cmdline/ctrl_r_spec.lua b/test/functional/cmdline/ctrl_r_spec.lua deleted file mode 100644 index a0f3955282..0000000000 --- a/test/functional/cmdline/ctrl_r_spec.lua +++ /dev/null @@ -1,34 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, insert, funcs, eq, feed = - helpers.clear, helpers.insert, helpers.funcs, helpers.eq, helpers.feed - -describe('cmdline CTRL-R', function() - before_each(clear) - - it('pasting non-special register inserts *between* lines', function() - insert([[ - line1abc - line2somemoretext - ]]) - -- Yank 2 lines linewise, then paste to cmdline. - feed([[gg0yj:0]]) - -- inserted between lines, NOT after the final line. - eq('line1abc\rline2somemoretext', funcs.getcmdline()) - - -- Yank 2 lines charwise, then paste to cmdline. - feed([[gg05lyvj:0]]) - -- inserted between lines, NOT after the final line. - eq('abc\rline2', funcs.getcmdline()) - - -- Yank 1 line linewise, then paste to cmdline. - feed([[ggyy:0]]) - -- No inserted. - eq('line1abc', funcs.getcmdline()) - end) - - it('pasting special register inserts , ', function() - feed([[:="foo\nbar\rbaz"]]) - eq('foo\nbar\rbaz', funcs.getcmdline()) - end) -end) - diff --git a/test/functional/cmdline/history_spec.lua b/test/functional/cmdline/history_spec.lua deleted file mode 100644 index ee2d36f642..0000000000 --- a/test/functional/cmdline/history_spec.lua +++ /dev/null @@ -1,36 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, meths, funcs, eq = - helpers.clear, helpers.meths, helpers.funcs, helpers.eq - -describe('history support code', function() - before_each(clear) - - it('correctly clears start of the history', function() - -- Regression test: check absence of the memory leak when clearing start of - -- the history using ex_getln.c/clr_history(). - eq(1, funcs.histadd(':', 'foo')) - eq(1, funcs.histdel(':')) - eq('', funcs.histget(':', -1)) - end) - - it('correctly clears end of the history', function() - -- Regression test: check absence of the memory leak when clearing end of - -- the history using ex_getln.c/clr_history(). - meths.set_option('history', 1) - eq(1, funcs.histadd(':', 'foo')) - eq(1, funcs.histdel(':')) - eq('', funcs.histget(':', -1)) - end) - - it('correctly removes item from history', function() - -- Regression test: check that ex_getln.c/del_history_idx() correctly clears - -- history index after removing history entry. If it does not then deleting - -- history will result in a double free. - eq(1, funcs.histadd(':', 'foo')) - eq(1, funcs.histadd(':', 'bar')) - eq(1, funcs.histadd(':', 'baz')) - eq(1, funcs.histdel(':', -2)) - eq(1, funcs.histdel(':')) - eq('', funcs.histget(':', -1)) - end) -end) diff --git a/test/functional/editor/K_spec.lua b/test/functional/editor/K_spec.lua new file mode 100644 index 0000000000..40f36491e4 --- /dev/null +++ b/test/functional/editor/K_spec.lua @@ -0,0 +1,38 @@ +local helpers = require('test.functional.helpers')(after_each) +local eq, clear, eval, feed, retry = + helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.retry + +describe('K', function() + local test_file = 'K_spec_out' + before_each(function() + clear() + os.remove(test_file) + end) + after_each(function() + os.remove(test_file) + end) + + it("invokes colon-prefixed 'keywordprg' as Vim command", function() + helpers.source([[ + let @a='fnord' + set keywordprg=:put]]) + + -- K on the text "a" resolves to `:put a`. + feed('iaK') + helpers.expect([[ + a + fnord]]) + end) + + it("invokes non-prefixed 'keywordprg' as shell command", function() + helpers.source([[ + let @a='fnord' + set keywordprg=echo\ fnord>>]]) + + -- K on the text "K_spec_out" resolves to `!echo fnord >> K_spec_out`. + feed('i'..test_file..'K') + retry(nil, nil, function() eq(1, eval('filereadable("'..test_file..'")')) end) + eq({'fnord'}, eval("readfile('"..test_file.."')")) + end) + +end) diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua new file mode 100644 index 0000000000..befad29922 --- /dev/null +++ b/test/functional/editor/completion_spec.lua @@ -0,0 +1,1196 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local assert_alive = helpers.assert_alive +local clear, feed = helpers.clear, helpers.feed +local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq +local feed_command, source, expect = helpers.feed_command, helpers.source, helpers.expect +local funcs = helpers.funcs +local curbufmeths = helpers.curbufmeths +local command = helpers.command +local meths = helpers.meths +local poke_eventloop = helpers.poke_eventloop + +describe('completion', function() + local screen + + before_each(function() + clear() + screen = Screen.new(60, 8) + screen:attach() + screen:set_default_attr_ids({ + [0] = {bold=true, foreground=Screen.colors.Blue}, + [1] = {background = Screen.colors.LightMagenta}, + [2] = {background = Screen.colors.Grey}, + [3] = {bold = true}, + [4] = {bold = true, foreground = Screen.colors.SeaGreen}, + [5] = {foreground = Screen.colors.Red}, + [6] = {background = Screen.colors.Black}, + [7] = {foreground = Screen.colors.White, background = Screen.colors.Red}, + [8] = {reverse = true}, + [9] = {bold = true, reverse = true}, + [10] = {foreground = Screen.colors.Grey0, background = Screen.colors.Yellow}, + }) + end) + + describe('v:completed_item', function() + it('is empty dict until completion', function() + eq({}, eval('v:completed_item')) + end) + it('is empty dict if the candidate is not inserted', function() + feed('ifooo') + screen:expect([[ + foo | + foo^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) The only match} | + ]]) + feed('') + screen:expect([[ + foo | + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + feed('') + eq({}, eval('v:completed_item')) + end) + it('returns expected dict in normal completion', function() + feed('ifooo') + eq('foo', eval('getline(2)')) + eq({word = 'foo', abbr = '', menu = '', + info = '', kind = '', user_data = ''}, + eval('v:completed_item')) + end) + it('is readonly', function() + screen:try_resize(80, 8) + feed('ifooo') + feed_command('let v:completed_item.word = "bar"') + neq(nil, string.find(eval('v:errmsg'), '^E46: ')) + feed_command('let v:errmsg = ""') + + feed_command('let v:completed_item.abbr = "bar"') + neq(nil, string.find(eval('v:errmsg'), '^E46: ')) + feed_command('let v:errmsg = ""') + + feed_command('let v:completed_item.menu = "bar"') + neq(nil, string.find(eval('v:errmsg'), '^E46: ')) + feed_command('let v:errmsg = ""') + + feed_command('let v:completed_item.info = "bar"') + neq(nil, string.find(eval('v:errmsg'), '^E46: ')) + feed_command('let v:errmsg = ""') + + feed_command('let v:completed_item.kind = "bar"') + neq(nil, string.find(eval('v:errmsg'), '^E46: ')) + feed_command('let v:errmsg = ""') + + feed_command('let v:completed_item.user_data = "bar"') + neq(nil, string.find(eval('v:errmsg'), '^E46: ')) + feed_command('let v:errmsg = ""') + end) + it('returns expected dict in omni completion', function() + source([[ + function! TestOmni(findstart, base) abort + return a:findstart ? 0 : [{'word': 'foo', 'abbr': 'bar', + \ 'menu': 'baz', 'info': 'foobar', 'kind': 'foobaz'}, + \ {'word': 'word', 'abbr': 'abbr', 'menu': 'menu', + \ 'info': 'info', 'kind': 'kind'}] + endfunction + setlocal omnifunc=TestOmni + ]]) + feed('i') + eq('foo', eval('getline(1)')) + screen:expect([[ + foo^ | + {2:bar foobaz baz }{0: }| + {1:abbr kind menu }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Omni completion (^O^N^P) }{4:match 1 of 2} | + ]]) + eq({word = 'foo', abbr = 'bar', menu = 'baz', + info = 'foobar', kind = 'foobaz', user_data = ''}, + eval('v:completed_item')) + end) + end) + + describe('completeopt', function() + before_each(function() + source([[ + function! TestComplete() abort + call complete(1, ['foo']) + return '' + endfunction + ]]) + end) + + it('inserts the first candidate if default', function() + feed_command('set completeopt+=menuone') + feed('ifooo') + screen:expect([[ + foo | + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + feed('') + -- the ^X prompt, only test this once + screen:expect([[ + foo | + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)} | + ]]) + feed('') + screen:expect([[ + foo | + foo^ | + {2:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) The only match} | + ]]) + feed('bar') + eq('foobar', eval('getline(2)')) + feed('o=TestComplete()') + screen:expect([[ + foo | + foobar | + foo^ | + {2:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + eq('foo', eval('getline(3)')) + end) + it('selects the first candidate if noinsert', function() + feed_command('set completeopt+=menuone,noinsert') + feed('ifooo') + screen:expect([[ + foo | + ^ | + {2:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) The only match} | + ]]) + feed('') + screen:expect([[ + foo | + foo^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + feed('') + eq('foo', eval('getline(2)')) + feed('o=TestComplete()') + screen:expect([[ + foo | + foo | + ^ | + {2:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + feed('') + eq('foo', eval('getline(3)')) + end) + it('does not insert the first candidate if noselect', function() + feed_command('set completeopt+=menuone,noselect') + feed('ifooo') + screen:expect([[ + foo | + ^ | + {1:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) }{5:Back at original} | + ]]) + feed('b') + screen:expect([[ + foo | + b^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) }{5:Back at original} | + ]]) + feed('ar') + eq('bar', eval('getline(2)')) + feed('o=TestComplete()') + screen:expect([[ + foo | + bar | + ^ | + {1:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + feed('bar') + eq('bar', eval('getline(3)')) + end) + it('does not select/insert the first candidate if noselect and noinsert', function() + feed_command('set completeopt+=menuone,noselect,noinsert') + feed('ifooo') + screen:expect([[ + foo | + ^ | + {1:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) }{5:Back at original} | + ]]) + feed('') + screen:expect([[ + foo | + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + eq('', eval('getline(2)')) + feed('o=TestComplete()') + screen:expect([[ + foo | + | + ^ | + {1:foo }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + feed('') + screen:expect([[ + foo | + | + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + eq('', eval('getline(3)')) + end) + it('does not change modified state if noinsert', function() + feed_command('set completeopt+=menuone,noinsert') + feed_command('setlocal nomodified') + feed('i=TestComplete()') + eq(0, eval('&l:modified')) + end) + it('does not change modified state if noselect', function() + feed_command('set completeopt+=menuone,noselect') + feed_command('setlocal nomodified') + feed('i=TestComplete()') + eq(0, eval('&l:modified')) + end) + end) + + describe('completeopt+=noinsert does not add blank undo items', function() + before_each(function() + source([[ + function! TestComplete() abort + call complete(1, ['foo', 'bar']) + return '' + endfunction + ]]) + feed_command('set completeopt+=noselect,noinsert') + feed_command('inoremap =TestComplete()') + end) + + local tests = { + [', , '] = {'', ''}, + [', , '] = {'', ''}, + } + + for name, seq in pairs(tests) do + it('using ' .. name, function() + feed('iaaa') + feed('A' .. seq[1] .. '') + feed('AA') + feed('Abbb') + feed('A' .. seq[2] .. '') + feed('AA') + feed('Accc') + feed('A' .. seq[1] .. '') + feed('AA') + + local expected = { + {'foo', 'bar', 'foo'}, + {'foo', 'bar', 'ccc'}, + {'foo', 'bar'}, + {'foo', 'bbb'}, + {'foo'}, + {'aaa'}, + {''}, + } + + for i = 1, #expected do + if i > 1 then + feed('u') + end + eq(expected[i], eval('getline(1, "$")')) + end + + for i = #expected, 1, -1 do + if i < #expected then + feed('') + end + eq(expected[i], eval('getline(1, "$")')) + end + end) + end + end) + + describe("refresh:always", function() + before_each(function() + source([[ + function! TestCompletion(findstart, base) abort + if a:findstart + let line = getline('.') + let start = col('.') - 1 + while start > 0 && line[start - 1] =~ '\a' + let start -= 1 + endwhile + return start + else + let ret = [] + for m in split("January February March April May June July August September October November December") + if m =~ a:base " match by regex + call add(ret, m) + endif + endfor + return {'words':ret, 'refresh':'always'} + endif + endfunction + + set completeopt=menuone,noselect + set completefunc=TestCompletion + ]]) + end ) + + it('completes on each input char', function () + feed('i') + screen:expect([[ + ^ | + {1:January }{6: }{0: }| + {1:February }{6: }{0: }| + {1:March }{6: }{0: }| + {1:April }{2: }{0: }| + {1:May }{2: }{0: }| + {1:June }{2: }{0: }| + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('u') + screen:expect([[ + u^ | + {1:January }{0: }| + {1:February }{0: }| + {1:June }{0: }| + {1:July }{0: }| + {1:August }{0: }| + {0:~ }| + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('g') + screen:expect([[ + ug^ | + {1:August }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('') + screen:expect([[ + ug^ | + {2:August }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- User defined completion (^U^N^P) The only match} | + ]]) + feed('') + screen:expect([[ + August^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + expect('August') + end) + + it("repeats correctly after backspace #2674", function () + feed('oJa') + screen:expect([[ + | + Ja^ | + {1:January }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('') + screen:expect([[ + | + J^ | + {1:January }{0: }| + {1:June }{0: }| + {1:July }{0: }| + {0:~ }| + {0:~ }| + {3:-- User defined completion (^U^N^P) }{5:Back at original} | + ]]) + feed('') + screen:expect([[ + | + January^ | + {2:January }{0: }| + {1:June }{0: }| + {1:July }{0: }| + {0:~ }| + {0:~ }| + {3:-- User defined completion (^U^N^P) }{4:match 1 of 3} | + ]]) + feed('') + screen:expect([[ + | + June^ | + {1:January }{0: }| + {2:June }{0: }| + {1:July }{0: }| + {0:~ }| + {0:~ }| + {3:-- User defined completion (^U^N^P) }{4:match 2 of 3} | + ]]) + feed('') + screen:expect([[ + | + Jun^e | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + feed('.') + screen:expect([[ + | + June | + Jun^e | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + expect([[ + + June + June]]) + end) + end) + + describe('with a lot of items', function() + before_each(function() + source([[ + function! TestComplete() abort + call complete(1, map(range(0,100), "string(v:val)")) + return '' + endfunction + ]]) + feed_command("set completeopt=menuone,noselect") + end) + + it("works", function() + feed('i=TestComplete()') + screen:expect([[ + ^ | + {1:0 }{6: }{0: }| + {1:1 }{2: }{0: }| + {1:2 }{2: }{0: }| + {1:3 }{2: }{0: }| + {1:4 }{2: }{0: }| + {1:5 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('7') + screen:expect([[ + 7^ | + {1:7 }{6: }{0: }| + {1:70 }{6: }{0: }| + {1:71 }{6: }{0: }| + {1:72 }{2: }{0: }| + {1:73 }{2: }{0: }| + {1:74 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('') + screen:expect([[ + 7^ | + {2:7 }{6: }{0: }| + {1:70 }{6: }{0: }| + {1:71 }{6: }{0: }| + {1:72 }{2: }{0: }| + {1:73 }{2: }{0: }| + {1:74 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('') + screen:expect([[ + 70^ | + {1:7 }{6: }{0: }| + {2:70 }{6: }{0: }| + {1:71 }{6: }{0: }| + {1:72 }{2: }{0: }| + {1:73 }{2: }{0: }| + {1:74 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + end) + + it('can be navigated with , ', function() + feed('i=TestComplete()') + screen:expect([[ + ^ | + {1:0 }{6: }{0: }| + {1:1 }{2: }{0: }| + {1:2 }{2: }{0: }| + {1:3 }{2: }{0: }| + {1:4 }{2: }{0: }| + {1:5 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('') + screen:expect([[ + ^ | + {1:0 }{6: }{0: }| + {1:1 }{2: }{0: }| + {1:2 }{2: }{0: }| + {2:3 }{0: }| + {1:4 }{2: }{0: }| + {1:5 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('') + screen:expect([[ + ^ | + {1:5 }{6: }{0: }| + {1:6 }{2: }{0: }| + {2:7 }{0: }| + {1:8 }{2: }{0: }| + {1:9 }{2: }{0: }| + {1:10 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('') + screen:expect([[ + ^ | + {1:5 }{6: }{0: }| + {1:6 }{2: }{0: }| + {1:7 }{2: }{0: }| + {2:8 }{0: }| + {1:9 }{2: }{0: }| + {1:10 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('') + screen:expect([[ + ^ | + {1:2 }{6: }{0: }| + {1:3 }{2: }{0: }| + {2:4 }{0: }| + {1:5 }{2: }{0: }| + {1:6 }{2: }{0: }| + {1:7 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('') -- stop on first item + screen:expect([[ + ^ | + {2:0 }{6: }{0: }| + {1:1 }{2: }{0: }| + {1:2 }{2: }{0: }| + {1:3 }{2: }{0: }| + {1:4 }{2: }{0: }| + {1:5 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('') -- when on first item, unselect + screen:expect([[ + ^ | + {1:0 }{6: }{0: }| + {1:1 }{2: }{0: }| + {1:2 }{2: }{0: }| + {1:3 }{2: }{0: }| + {1:4 }{2: }{0: }| + {1:5 }{2: }{0: }| + {3:-- INSERT --} | + ]]) + feed('') -- when unselected, select last item + screen:expect([[ + ^ | + {1:95 }{2: }{0: }| + {1:96 }{2: }{0: }| + {1:97 }{2: }{0: }| + {1:98 }{2: }{0: }| + {1:99 }{2: }{0: }| + {2:100 }{6: }{0: }| + {3:-- INSERT --} | + ]]) + feed('') + screen:expect([[ + ^ | + {1:94 }{2: }{0: }| + {1:95 }{2: }{0: }| + {2:96 }{0: }| + {1:97 }{2: }{0: }| + {1:98 }{2: }{0: }| + {1:99 }{6: }{0: }| + {3:-- INSERT --} | + ]]) + feed('') + screen:expect([[ + 96^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + end) + end) + + it("does not indent until an item is selected #8345", function () + -- Indents on "ind", unindents on "unind". + source([[ + function! TestIndent() + let line = getline(v:lnum) + if (line =~ '^\s*ind') + return indent(v:lnum-1) + shiftwidth() + elseif (line =~ '^\s*unind') + return indent(v:lnum-1) - shiftwidth() + else + return indent(v:lnum-1) + endif + endfunction + set indentexpr=TestIndent() + set indentkeys=o,O,!^F,=ind,=unind + set completeopt+=menuone + ]]) + + -- Give some words to complete. + feed("iinc uninc indent unindent") + + -- Does not indent when "ind" is typed. + feed("in") + -- Completion list is generated incorrectly if we send everything at once + -- via nvim_input(). So poke_eventloop() before sending . #8480 + poke_eventloop() + feed("d") + + screen:expect([[ + inc uninc indent unindent | + ind^ | + {2:indent }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 2} | + ]]) + + -- Indents when the item is selected + feed("") + screen:expect([[ + inc uninc indent unindent | + indent^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + -- Indents when completion is exited using ESC. + feed("ind") + screen:expect([[ + inc uninc indent unindent | + indent | + in^d | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + -- Works for unindenting too. + feed("ounin") + helpers.poke_eventloop() + feed("d") + screen:expect([[ + inc uninc indent unindent | + indent | + ind | + unind^ | + {0:~ }{2: unindent }{0: }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 2} | + ]]) + -- Works when going back and forth. + feed("c") + screen:expect([[ + inc uninc indent unindent | + indent | + ind | + uninc^ | + {0:~ }{2: uninc }{0: }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 2} | + ]]) + feed("d") + screen:expect([[ + inc uninc indent unindent | + indent | + ind | + unind^ | + {0:~ }{2: unindent }{0: }| + {0:~ }| + {0:~ }| + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 2} | + ]]) + feed("") + screen:expect([[ + inc uninc indent unindent | + indent | + ind | + uninden^t | + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + end) + + it('disables folding during completion', function () + feed_command("set foldmethod=indent") + feed('ifoobargg') + screen:expect([[ + ^foo | + bar | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + feed('A') + screen:expect([[ + foo^ | + bar | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Whole line completion (^L^N^P) }{7:Pattern not found} | + ]]) + eq(-1, eval('foldclosed(1)')) + end) + + it('popupmenu is not interrupted by events', function () + feed_command("set complete=.") + + feed('ifoobar fooeggf') + screen:expect([[ + foobar fooegg | + fooegg^ | + {1:foobar }{0: }| + {2:fooegg }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{4:match 1 of 2} | + ]]) + + assert_alive() + -- popupmenu still visible + screen:expect{grid=[[ + foobar fooegg | + fooegg^ | + {1:foobar }{0: }| + {2:fooegg }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{4:match 1 of 2} | + ]], unchanged=true} + + feed('') + -- Didn't restart completion: old matches still used + screen:expect([[ + foobar fooegg | + foobar^ | + {2:foobar }{0: }| + {1:fooegg }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{4:match 2 of 2} | + ]]) + end) + + describe('lua completion', function() + it('expands when there is only one match', function() + feed(':lua CURRENT_TESTING_VAR = 1') + feed(':lua CURRENT_TESTING_') + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + :lua CURRENT_TESTING_VAR^ | + ]]} + end) + + it('expands when there is only one match', function() + feed(':lua CURRENT_TESTING_FOO = 1') + feed(':lua CURRENT_TESTING_BAR = 1') + feed(':lua CURRENT_TESTING_') + screen:expect{ grid = [[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {10:CURRENT_TESTING_BAR}{9: CURRENT_TESTING_FOO }| + :lua CURRENT_TESTING_BAR^ | + ]], unchanged = true } + end) + + it('provides completion from `getcompletion()`', function() + eq({'vim'}, funcs.getcompletion('vi', 'lua')) + eq({'api'}, funcs.getcompletion('vim.ap', 'lua')) + eq({'tbl_filter'}, funcs.getcompletion('vim.tbl_fil', 'lua')) + eq({'vim'}, funcs.getcompletion('print(vi', 'lua')) + end) + end) + + describe('from the commandline window', function() + it('is cleared after CTRL-C', function () + feed('q:') + feed('ifoo faa fee f') + screen:expect([[ + | + {8:[No Name] }| + {0::}foo faa fee f^ | + {0:~ }| + {0:~ }| + {0:~ }| + {9:[Command Line] }| + {3:-- INSERT --} | + ]] ) + feed('') + screen:expect([[ + | + {8:[No Name] }| + {0::}foo faa fee foo^ | + {0:~ }{2: foo }{0: }| + {0:~ }{1: faa }{0: }| + {0:~ }{1: fee }{0: }| + {9:[Command Line] }| + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 3} | + ]]) + feed('') + screen:expect([[ + | + {8:[No Name] }| + {0::}foo faa fee foo | + {0:~ }| + {0:~ }| + {0:~ }| + {9:[Command Line] }| + :foo faa fee foo^ | + ]]) + end) + end) + + describe('with numeric items', function() + before_each(function() + source([[ + function! TestComplete() abort + call complete(1, g:_complist) + return '' + endfunction + ]]) + meths.set_option('completeopt', 'menuone,noselect') + meths.set_var('_complist', {{ + word=0, + abbr=1, + menu=2, + kind=3, + info=4, + icase=5, + dup=6, + empty=7, + }}) + end) + + it('shows correct variant as word', function() + feed('i=TestComplete()') + screen:expect([[ + ^ | + {1:1 3 2 }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + end) + end) + + it("'ignorecase' 'infercase' CTRL-X CTRL-N #6451", function() + feed_command('set ignorecase infercase') + feed_command('edit BACKERS.md') + feed('oX') + screen:expect([[ + # Bountysource Backers | + Xnull^ | + {2:Xnull }{6: } | + {1:Xoxomoon }{6: }ryone who backed our [Bountysource fundraise| + {1:Xu }{6: }ountysource.com/teams/neovim/fundraiser)! | + {1:Xpayn }{2: } | + {1:Xinity }{2: }d URL in BACKERS.md. | + {3:-- Keyword Local completion (^N^P) }{4:match 1 of 7} | + ]]) + end) + + it('TextChangedP autocommand', function() + curbufmeths.set_lines(0, 1, false, { 'foo', 'bar', 'foobar'}) + source([[ + set complete=. completeopt=menuone + let g:foo = [] + autocmd! TextChanged * :call add(g:foo, "N") + autocmd! TextChangedI * :call add(g:foo, "I") + autocmd! TextChangedP * :call add(g:foo, "P") + call cursor(3, 1) + ]]) + + command('let g:foo = []') + feed('o') + poke_eventloop() + feed('') + eq({'I'}, eval('g:foo')) + + command('let g:foo = []') + feed('S') + poke_eventloop() + feed('f') + poke_eventloop() + eq({'I', 'I'}, eval('g:foo')) + feed('') + + command('let g:foo = []') + feed('S') + poke_eventloop() + feed('f') + poke_eventloop() + feed('') + poke_eventloop() + eq({'I', 'I', 'P'}, eval('g:foo')) + feed('') + + command('let g:foo = []') + feed('S') + poke_eventloop() + feed('f') + poke_eventloop() + feed('') + poke_eventloop() + feed('') + poke_eventloop() + eq({'I', 'I', 'P', 'P'}, eval('g:foo')) + feed('') + + command('let g:foo = []') + feed('S') + poke_eventloop() + feed('f') + poke_eventloop() + feed('') + poke_eventloop() + feed('') + poke_eventloop() + feed('') + poke_eventloop() + eq({'I', 'I', 'P', 'P', 'P'}, eval('g:foo')) + feed('') + + command('let g:foo = []') + feed('S') + poke_eventloop() + feed('f') + poke_eventloop() + feed('') + poke_eventloop() + feed('') + poke_eventloop() + feed('') + poke_eventloop() + feed('') + eq({'I', 'I', 'P', 'P', 'P', 'P'}, eval('g:foo')) + feed('') + + eq({'foo', 'bar', 'foobar', 'foo'}, eval('getline(1, "$")')) + + source([[ + au! TextChanged + au! TextChangedI + au! TextChangedP + set complete&vim completeopt&vim + ]]) + end) + + it('CompleteChanged autocommand', function() + curbufmeths.set_lines(0, 1, false, { 'foo', 'bar', 'foobar', ''}) + source([[ + set complete=. completeopt=noinsert,noselect,menuone + function! OnPumChange() + let g:event = copy(v:event) + let g:item = get(v:event, 'completed_item', {}) + let g:word = get(g:item, 'word', v:null) + endfunction + autocmd! CompleteChanged * :call OnPumChange() + call cursor(4, 1) + ]]) + + feed('Sf') + screen:expect([[ + foo | + bar | + foobar | + f^ | + {1:foo }{0: }| + {1:foobar }{0: }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{5:Back at original} | + ]]) + eq({completed_item = {}, width = 15, + height = 2, size = 2, + col = 0, row = 4, scrollbar = false}, + eval('g:event')) + feed('') + screen:expect([[ + foo | + bar | + foobar | + foo^ | + {2:foo }{0: }| + {1:foobar }{0: }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{4:match 1 of 2} | + ]]) + eq('foo', eval('g:word')) + feed('') + screen:expect([[ + foo | + bar | + foobar | + foobar^ | + {1:foo }{0: }| + {2:foobar }{0: }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{4:match 2 of 2} | + ]]) + eq('foobar', eval('g:word')) + feed('') + screen:expect([[ + foo | + bar | + foobar | + foobar^ | + {2:foo }{0: }| + {1:foobar }{0: }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{4:match 1 of 2} | + ]]) + eq('foo', eval('g:word')) + feed('') + screen:expect([[ + foo | + bar | + foobar | + foobar^ | + {1:foo }{0: }| + {2:foobar }{0: }| + {0:~ }| + {3:-- Keyword completion (^N^P) }{4:match 2 of 2} | + ]]) + eq('foobar', eval('g:word')) + feed('') + end) +end) diff --git a/test/functional/editor/count_spec.lua b/test/functional/editor/count_spec.lua new file mode 100644 index 0000000000..94f741250a --- /dev/null +++ b/test/functional/editor/count_spec.lua @@ -0,0 +1,39 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local eval = helpers.eval +local feed = helpers.feed +local clear = helpers.clear +local command = helpers.command + +describe('v:count/v:count1', function() + before_each(function() + clear() + + command('map _x :let g:count = "v:count=". v:count .", v:count1=". v:count1') + end) + + describe('in cmdwin', function() + it('equal 0/1 when no count is given', function() + feed('q:_x') + eq('v:count=0, v:count1=1', eval('g:count')) + end) + + it('equal 2/2 when count of 2 is given', function() + feed('q:2_x') + eq('v:count=2, v:count1=2', eval('g:count')) + end) + end) + + describe('in normal mode', function() + it('equal 0/1 when no count is given', function() + feed('_x') + eq('v:count=0, v:count1=1', eval('g:count')) + end) + + it('equal 2/2 when count of 2 is given', function() + feed('2_x') + eq('v:count=2, v:count1=2', eval('g:count')) + end) + end) +end) diff --git a/test/functional/editor/fold_spec.lua b/test/functional/editor/fold_spec.lua new file mode 100644 index 0000000000..00e83bedc8 --- /dev/null +++ b/test/functional/editor/fold_spec.lua @@ -0,0 +1,362 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local insert = helpers.insert +local feed = helpers.feed +local expect = helpers.expect +local feed_command = helpers.feed_command +local funcs = helpers.funcs +local foldlevel = funcs.foldlevel +local foldclosedend = funcs.foldclosedend +local eq = helpers.eq + +describe('Folds', function() + local tempfname = 'Xtest-fold.txt' + clear() + before_each(function() feed_command('enew!') end) + after_each(function() os.remove(tempfname) end) + it('manual folding adjusts with filter', function() + insert([[ + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20]]) + feed_command('4,$fold', '%foldopen', '10,$fold', '%foldopen') + feed_command('1,8! cat') + feed('5ggzdzMGdd') + expect([[ + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9]]) + end) + describe('adjusting folds after :move', function() + local function manually_fold_indent() + -- setting foldmethod twice is a trick to get vim to set the folds for me + feed_command('set foldmethod=indent', 'set foldmethod=manual') + -- Ensure that all folds will get closed (makes it easier to test the + -- length of folds). + feed_command('set foldminlines=0') + -- Start with all folds open (so :move ranges aren't affected by closed + -- folds). + feed_command('%foldopen!') + end + + local function get_folds() + local rettab = {} + for i = 1, funcs.line('$') do + table.insert(rettab, foldlevel(i)) + end + return rettab + end + + local function test_move_indent(insert_string, move_command) + -- This test is easy because we just need to ensure that the resulting + -- fold is the same as calculated when creating folds from scratch. + insert(insert_string) + feed_command(move_command) + local after_move_folds = get_folds() + -- Doesn't change anything, but does call foldUpdateAll() + feed_command('set foldminlines=0') + eq(after_move_folds, get_folds()) + -- Set up the buffer with insert_string for the manual fold testing. + feed_command('enew!') + insert(insert_string) + manually_fold_indent() + feed_command(move_command) + end + + it('neither closes nor corrupts folds', function() + test_move_indent([[ +a + a + a + a + a + a +a + a + a + a + a + a +a + a + a + a + a + a]], '7,12m0') + expect([[ +a + a + a + a + a + a +a + a + a + a + a + a +a + a + a + a + a + a]]) + -- lines are not closed, folds are correct + for i = 1,funcs.line('$') do + eq(-1, funcs.foldclosed(i)) + if i == 1 or i == 7 or i == 13 then + eq(0, foldlevel(i)) + elseif i == 4 then + eq(2, foldlevel(i)) + else + eq(1, foldlevel(i)) + end + end + -- folds are not corrupted + feed('zM') + eq(6, foldclosedend(2)) + eq(12, foldclosedend(8)) + eq(18, foldclosedend(14)) + end) + it("doesn't split a fold when the move is within it", function() + test_move_indent([[ +a + a + a + a + a + a + a + a + a +a]], '5m6') + eq({0, 1, 1, 2, 2, 2, 2, 1, 1, 0}, get_folds()) + end) + it('truncates folds that end in the moved range', function() + test_move_indent([[ +a + a + a + a + a +a +a]], '4,5m6') + eq({0, 1, 2, 0, 0, 0, 0}, get_folds()) + end) + it('moves folds that start between moved range and destination', function() + test_move_indent([[ +a + a + a + a + a +a +a + a + a + a +a +a + a]], '3,4m$') + eq({0, 1, 1, 0, 0, 1, 2, 1, 0, 0, 1, 0, 0}, get_folds()) + end) + it('does not affect folds outside changed lines', function() + test_move_indent([[ + a + a + a +a +a +a + a + a + a]], '4m5') + eq({1, 1, 1, 0, 0, 0, 1, 1, 1}, get_folds()) + end) + it('moves and truncates folds that start in moved range', function() + test_move_indent([[ +a + a + a + a + a +a +a +a +a +a]], '1,3m7') + eq({0, 0, 0, 0, 0, 1, 2, 0, 0, 0}, get_folds()) + end) + it('breaks a fold when moving text into it', function() + test_move_indent([[ +a + a + a + a + a +a +a]], '$m4') + eq({0, 1, 2, 2, 0, 0, 0}, get_folds()) + end) + it('adjusts correctly when moving a range backwards', function() + test_move_indent([[ +a + a + a + a +a]], '2,3m0') + eq({1, 2, 0, 0, 0}, get_folds()) + end) + it('handles shifting all remaining folds', function() + test_move_indent([[ + a + a + a + a + a + a + a + a + a + a + a + a + a + a +a]], '13m7') + eq({1, 2, 2, 2, 1, 2, 2, 1, 1, 1, 2, 2, 2, 1, 0}, get_folds()) + end) + end) + it('updates correctly on :read', function() + -- luacheck: ignore 621 + helpers.write_file(tempfname, [[ + a + + + a]]) + insert([[ + a + a + a + a + ]]) + feed_command('set foldmethod=indent', '2', '%foldopen') + feed_command('read ' .. tempfname) + -- Just to check we have the correct file text. + expect([[ + a + a + a + + + a + a + a + ]]) + for i = 1,2 do + eq(1, funcs.foldlevel(i)) + end + for i = 3,5 do + eq(0, funcs.foldlevel(i)) + end + for i = 6,8 do + eq(1, funcs.foldlevel(i)) + end + end) + it('combines folds when removing separating space', function() + -- luacheck: ignore 621 + insert([[ + a + a + a + a + a + a + a + a + ]]) + feed_command('set foldmethod=indent', '3,5d') + eq(5, funcs.foldclosedend(1)) + end) + it("doesn't combine folds that have a specified end", function() + insert([[ + {{{ + }}} + + + + {{{ + + }}} + ]]) + feed_command('set foldmethod=marker', '3,5d', '%foldclose') + eq(2, funcs.foldclosedend(1)) + end) + it('splits folds according to >N and 1' + endif + return 0 + endfunction + ]]) + helpers.write_file(tempfname, [[ + b + b + a + a + d + a + a + c]]) + insert([[ + a + a + a + a + a + a + ]]) + feed_command('set foldmethod=expr', 'set foldexpr=TestFoldExpr(v:lnum)', '2', 'foldopen') + feed_command('read ' .. tempfname, '%foldclose') + eq(2, funcs.foldclosedend(1)) + eq(0, funcs.foldlevel(3)) + eq(0, funcs.foldlevel(4)) + eq(6, funcs.foldclosedend(5)) + eq(10, funcs.foldclosedend(7)) + eq(14, funcs.foldclosedend(11)) + end) +end) diff --git a/test/functional/editor/jump_spec.lua b/test/functional/editor/jump_spec.lua new file mode 100644 index 0000000000..9e7158e2f7 --- /dev/null +++ b/test/functional/editor/jump_spec.lua @@ -0,0 +1,139 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq +local funcs = helpers.funcs +local feed = helpers.feed +local redir_exec = helpers.redir_exec +local write_file = helpers.write_file + +describe('jumplist', function() + local fname1 = 'Xtest-functional-normal-jump' + local fname2 = fname1..'2' + before_each(clear) + after_each(function() + os.remove(fname1) + os.remove(fname2) + end) + + it('does not add a new entry on startup', function() + eq('\n jump line col file/text\n>', funcs.execute('jumps')) + end) + + it('does not require two strokes to jump back', function() + write_file(fname1, 'first file contents') + write_file(fname2, 'second file contents') + + command('args '..fname1..' '..fname2) + local buf1 = funcs.bufnr(fname1) + local buf2 = funcs.bufnr(fname2) + + command('next') + feed('') + eq(buf1, funcs.bufnr('%')) + + command('first') + command('snext') + feed('') + eq(buf1, funcs.bufnr('%')) + feed('') + eq(buf2, funcs.bufnr('%')) + feed('') + eq(buf1, funcs.bufnr('%')) + + command('drop '..fname2) + feed('') + eq(buf1, funcs.bufnr('%')) + end) +end) + +describe("jumpoptions=stack behaves like 'tagstack'", function() + before_each(function() + clear() + feed(':clearjumps') + + -- Add lines so that we have locations to jump to. + for i = 1,101,1 + do + feed('iLine ' .. i .. '') + end + + -- Jump around to add some locations to the jump list. + feed('0gg') + feed('10gg') + feed('20gg') + feed('30gg') + feed('40gg') + feed('50gg') + + feed(':set jumpoptions=stack') + end) + + after_each(function() + feed('set jumpoptions=') + end) + + it('discards the tail when navigating from the middle', function() + feed('') + feed('') + + eq( '\n' + .. ' jump line col file/text\n' + .. ' 4 102 0 \n' + .. ' 3 1 0 Line 1\n' + .. ' 2 10 0 Line 10\n' + .. ' 1 20 0 Line 20\n' + .. '> 0 30 0 Line 30\n' + .. ' 1 40 0 Line 40\n' + .. ' 2 50 0 Line 50', + redir_exec('jumps')) + + feed('90gg') + + eq( '\n' + .. ' jump line col file/text\n' + .. ' 5 102 0 \n' + .. ' 4 1 0 Line 1\n' + .. ' 3 10 0 Line 10\n' + .. ' 2 20 0 Line 20\n' + .. ' 1 30 0 Line 30\n' + .. '>', + redir_exec('jumps')) + end) + + it('does not add the same location twice adjacently', function() + feed('60gg') + feed('60gg') + + eq( '\n' + .. ' jump line col file/text\n' + .. ' 7 102 0 \n' + .. ' 6 1 0 Line 1\n' + .. ' 5 10 0 Line 10\n' + .. ' 4 20 0 Line 20\n' + .. ' 3 30 0 Line 30\n' + .. ' 2 40 0 Line 40\n' + .. ' 1 50 0 Line 50\n' + .. '>', + redir_exec('jumps')) + end) + + it('does add the same location twice nonadjacently', function() + feed('10gg') + feed('20gg') + + eq( '\n' + .. ' jump line col file/text\n' + .. ' 8 102 0 \n' + .. ' 7 1 0 Line 1\n' + .. ' 6 10 0 Line 10\n' + .. ' 5 20 0 Line 20\n' + .. ' 4 30 0 Line 30\n' + .. ' 3 40 0 Line 40\n' + .. ' 2 50 0 Line 50\n' + .. ' 1 10 0 Line 10\n' + .. '>', + redir_exec('jumps')) + end) +end) diff --git a/test/functional/editor/lang_spec.lua b/test/functional/editor/lang_spec.lua new file mode 100644 index 0000000000..24d1262f5f --- /dev/null +++ b/test/functional/editor/lang_spec.lua @@ -0,0 +1,63 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear, insert, eq = helpers.clear, helpers.insert, helpers.eq +local command, expect = helpers.command, helpers.expect +local feed, eval = helpers.feed, helpers.eval +local exc_exec = helpers.exc_exec + +describe('gu and gU', function() + before_each(clear) + + it('works in any locale with default casemap', function() + eq('internal,keepascii', eval('&casemap')) + insert("iI") + feed("VgU") + expect("II") + feed("Vgu") + expect("ii") + end) + + describe('works in Turkish locale', function() + clear() + + local err = exc_exec('lang ctype tr_TR.UTF-8') + if err ~= 0 then + pending("Locale tr_TR.UTF-8 not supported", function() end) + return + end + + before_each(function() + command('lang ctype tr_TR.UTF-8') + end) + + it('with default casemap', function() + eq('internal,keepascii', eval('&casemap')) + -- expect ASCII behavior + insert("iI") + feed("VgU") + expect("II") + feed("Vgu") + expect("ii") + end) + + it('with casemap=""', function() + command('set casemap=') + -- expect either Turkish locale behavior or ASCII behavior + local iupper = eval("toupper('i')") + if iupper == "İ" then + insert("iI") + feed("VgU") + expect("İI") + feed("Vgu") + expect("iı") + elseif iupper == "I" then + insert("iI") + feed("VgU") + expect("II") + feed("Vgu") + expect("ii") + else + error("expected toupper('i') to be either 'I' or 'İ'") + end + end) + end) +end) diff --git a/test/functional/editor/langmap_spec.lua b/test/functional/editor/langmap_spec.lua new file mode 100644 index 0000000000..e4349a22e7 --- /dev/null +++ b/test/functional/editor/langmap_spec.lua @@ -0,0 +1,280 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq, neq, call = helpers.eq, helpers.neq, helpers.call +local eval, feed, clear = helpers.eval, helpers.feed, helpers.clear +local command, insert, expect = helpers.command, helpers.insert, helpers.expect +local feed_command = helpers.feed_command +local curwin = helpers.curwin + +describe("'langmap'", function() + before_each(function() + clear() + insert('iii www') + command('set langmap=iw,wi') + feed('gg0') + end) + + it("converts keys in normal mode", function() + feed('ix') + expect('iii ww') + feed('whello') + expect('iii helloww') + end) + it("gives characters that are mapped by :nmap.", function() + command('map i 0x') + feed('w') + expect('ii www') + end) + describe("'langnoremap' option.", function() + before_each(function() + command('nmapclear') + end) + it("'langnoremap' is by default ON", function() + eq(eval('&langnoremap'), 1) + end) + it("Results of maps are not converted when 'langnoremap' ON.", + function() + command('nmap x i') + feed('xdl') + expect('dliii www') + end) + it("applies when deciding whether to map recursively", function() + command('nmap l i') + command('nmap w j') + feed('ll') + expect('liii www') + end) + it("does not stop applying 'langmap' on first character of a mapping", + function() + command('1t1') + command('1t1') + command('goto 1') + command('nmap w j') + feed('iiahello') + expect([[ + iii www + iii www + ihelloii www]]) + end) + it("Results of maps are converted when 'langnoremap' OFF.", + function() + command('set nolangnoremap') + command('nmap x i') + feed('xdl') + expect('iii ww') + end) + end) + -- e.g. CTRL-W_j , mj , 'j and "jp + it('conversions are applied to keys in middle of command', + function() + -- Works in middle of window command + feed('s') + local origwin = curwin() + feed('i') + neq(curwin(), origwin) + -- Works when setting a mark + feed('yy3p3gg0mwgg0mi') + eq(call('getpos', "'i"), {0, 3, 1, 0}) + eq(call('getpos', "'w"), {0, 1, 1, 0}) + feed('3dd') + -- Works when moving to a mark + feed("'i") + eq(call('getpos', '.'), {0, 1, 1, 0}) + -- Works when selecting a register + feed('qillqqwhhq') + eq(eval('@i'), 'hh') + eq(eval('@w'), 'll') + feed('ai') + expect('illii www') + feed('"ip') + expect('illllii www') + -- Works with i_CTRL-O + feed('0aihi') + expect('illllii hiwww') + end) + + describe('exceptions', function() + -- All "command characters" that 'langmap' does not apply to. + -- These tests consist of those places where some subset of ASCII + -- characters define certain commands, yet 'langmap' is not applied to + -- them. + -- n.b. I think these shouldn't be exceptions. + it(':s///c confirmation', function() + command('set langmap=yn,ny') + feed('qa') + feed_command('s/i/w/gc') + feed('yynq') + expect('wwi www') + feed('u@a') + expect('wwi www') + eq(eval('@a'), ':s/i/w/gc\ryyn') + end) + it('insert-mode CTRL-G', function() + command('set langmap=jk,kj') + command('d') + insert([[ + hello + hello + hello]]) + expect([[ + hello + hello + hello]]) + feed('qa') + feed('gg3|ahellojx') + feed('q') + expect([[ + helhellolo + helxlo + hello]]) + eq(eval('@a'), 'gg3|ahellojx') + end) + it('command-line CTRL-\\', function() + command('set langmap=en,ne') + feed(':e\'hello\'\rput =""') + expect([[ + iii www + hello]]) + end) + it('command-line CTRL-R', function() + helpers.source([[ + let i_value = 0 + let j_value = 0 + call setreg('i', 'i_value') + call setreg('j', 'j_value') + set langmap=ij,ji + ]]) + feed(':let i=1') + eq(eval('i_value'), 1) + eq(eval('j_value'), 0) + end) + -- it('-- More -- prompt', function() + -- -- The 'b' 'j' 'd' 'f' commands at the -- More -- prompt + -- end) + it('ask yes/no after backwards range', function() + command('set langmap=yn,ny') + feed('dd') + insert([[ + hello + there + these + are + some + lines + ]]) + feed_command('4,2d') + feed('n') + expect([[ + hello + there + these + are + some + lines + ]]) + end) + it('prompt for number', function() + command('set langmap=12,21') + helpers.source([[ + let gotten_one = 0 + function Map() + let answer = inputlist(['a', '1.', '2.', '3.']) + if answer == 1 + let g:gotten_one = 1 + endif + endfunction + nnoremap x :call Map() + ]]) + feed('x1') + eq(eval('gotten_one'), 1) + command('let g:gotten_one = 0') + feed_command('call Map()') + feed('1') + eq(eval('gotten_one'), 1) + end) + end) + it('conversions are not applied during setreg()', + function() + call('setreg', 'i', 'ww') + eq(eval('@i'), 'ww') + end) + it('conversions not applied in insert mode', function() + feed('aiiiwww') + expect('iiiiwwwii www') + end) + it('conversions not applied in search mode', function() + feed('/iiix') + expect('ii www') + end) + it('conversions not applied in cmdline mode', function() + feed(':call append(1, "iii")') + expect([[ + iii www + iii]]) + end) + + local function testrecording(command_string, expect_string, setup_function) + if setup_function then setup_function() end + feed('qa' .. command_string .. 'q') + expect(expect_string) + eq(helpers.funcs.nvim_replace_termcodes(command_string, true, true, true), + eval('@a')) + if setup_function then setup_function() end + -- n.b. may need nvim_replace_termcodes() here. + feed('@a') + expect(expect_string) + end + + local function local_setup() + -- Can't use `insert` as it uses `i` and we've swapped the meaning of that + -- with the `langmap` setting. + command('%d') + command("put ='hello'") + command('1d') + end + + it('does not affect recording special keys', function() + testrecording('A', 'hell', local_setup) + testrecording('>>', 'hello', local_setup) + command('nnoremap \\ x') + testrecording('\\', 'ello', local_setup) + testrecording('A', 'hello', local_setup) + end) + pending('Translates modified keys correctly', function() + command('nnoremap x') + command('nnoremap l') + testrecording('', 'ello', local_setup) + testrecording('x', 'hllo', local_setup) + end) + pending('handles multi-byte characters', function() + command('set langmap=ïx') + testrecording('ï', 'ello', local_setup) + -- The test below checks that what's recorded is correct. + -- It doesn't check the behaviour, as in order to cause some behaviour we + -- need to map the multi-byte character, and there is a known bug + -- preventing this from working (see the test below). + command('set langmap=xï') + testrecording('x', 'hello', local_setup) + end) + pending('handles multibyte mappings', function() + -- See this vim issue for the problem, may as well add a test. + -- https://github.com/vim/vim/issues/297 + command('set langmap=ïx') + command('nnoremap x diw') + testrecording('ï', '', local_setup) + command('set nolangnoremap') + command('set langmap=xï') + command('nnoremap ï ix') + testrecording('x', 'xhello', local_setup) + end) + -- This test is to ensure the behaviour doesn't change from what's already + -- around. I (hardenedapple) personally think this behaviour should be + -- changed. + it('treats control modified keys as characters', function() + command('nnoremap iw') + command('nnoremap ii') + testrecording('', 'whello', local_setup) + testrecording('', 'ihello', local_setup) + end) + +end) diff --git a/test/functional/editor/macro_spec.lua b/test/functional/editor/macro_spec.lua new file mode 100644 index 0000000000..102d8fc723 --- /dev/null +++ b/test/functional/editor/macro_spec.lua @@ -0,0 +1,30 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local eval = helpers.eval +local feed = helpers.feed +local clear = helpers.clear +local expect = helpers.expect +local command = helpers.command + +describe('macros', function() + before_each(clear) + it('can be recorded and replayed', function() + feed('qiahelloq') + expect('hello') + eq(eval('@i'), 'ahello') + feed('@i') + expect('hellohello') + eq(eval('@i'), 'ahello') + end) + it('applies maps', function() + command('imap x l') + command('nmap l a') + feed('qilxxxq') + expect('lll') + eq(eval('@i'), 'lxxx') + feed('@i') + expect('llllll') + eq(eval('@i'), 'lxxx') + end) +end) diff --git a/test/functional/editor/meta_key_spec.lua b/test/functional/editor/meta_key_spec.lua new file mode 100644 index 0000000000..2a9541ba96 --- /dev/null +++ b/test/functional/editor/meta_key_spec.lua @@ -0,0 +1,55 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local command = helpers.command +local expect = helpers.expect +local funcs = helpers.funcs +local eq = helpers.eq + +describe('meta-keys #8226 #13042', function() + before_each(function() + clear() + end) + + it('ALT/META, normal-mode', function() + -- Unmapped ALT-chords behave as ESC+c + insert('hello') + feed('0') + expect('llo') + -- Mapped ALT-chord behaves as mapped. + command('nnoremap Ameta-l') + command('nnoremap Aalt-j') + feed('') + expect('lloalt-jmeta-l') + end) + + it('ALT/META, visual-mode', function() + -- Unmapped ALT-chords behave as ESC+c + insert('peaches') + feed('viwviw') + expect('peach') + -- Mapped ALT-chord behaves as mapped. + command('vnoremap Ameta-l') + command('vnoremap Aalt-j') + feed('viwviw') + expect('peachalt-jmeta-l') + end) + + it('ALT/META insert-mode', function() + -- Mapped ALT-chord behaves as mapped. + command('inoremap meta-l') + command('inoremap alt-j') + feed('i xxx a') + expect('meta-l xxx alt-j') + eq({ 0, 1, 14, 0, }, funcs.getpos('.')) + -- Unmapped ALT-chord behaves as ESC+c. + command('iunmap ') + feed('0i') + eq({ 0, 1, 2, 0, }, funcs.getpos('.')) + -- Unmapped ALT-chord has same `undo` characteristics as ESC+ + command('0,$d') + feed('ahello') + expect('hellohello') + feed('u') + expect('hello') + end) +end) diff --git a/test/functional/editor/mode_cmdline_spec.lua b/test/functional/editor/mode_cmdline_spec.lua new file mode 100644 index 0000000000..0f7d821bb5 --- /dev/null +++ b/test/functional/editor/mode_cmdline_spec.lua @@ -0,0 +1,69 @@ +-- Cmdline-mode tests. + +local helpers = require('test.functional.helpers')(after_each) +local clear, insert, funcs, eq, feed = + helpers.clear, helpers.insert, helpers.funcs, helpers.eq, helpers.feed +local meths = helpers.meths + +describe('cmdline CTRL-R', function() + before_each(clear) + + it('pasting non-special register inserts *between* lines', function() + insert([[ + line1abc + line2somemoretext + ]]) + -- Yank 2 lines linewise, then paste to cmdline. + feed([[gg0yj:0]]) + -- inserted between lines, NOT after the final line. + eq('line1abc\rline2somemoretext', funcs.getcmdline()) + + -- Yank 2 lines charwise, then paste to cmdline. + feed([[gg05lyvj:0]]) + -- inserted between lines, NOT after the final line. + eq('abc\rline2', funcs.getcmdline()) + + -- Yank 1 line linewise, then paste to cmdline. + feed([[ggyy:0]]) + -- No inserted. + eq('line1abc', funcs.getcmdline()) + end) + + it('pasting special register inserts , ', function() + feed([[:="foo\nbar\rbaz"]]) + eq('foo\nbar\rbaz', funcs.getcmdline()) + end) +end) + +describe('cmdline history', function() + before_each(clear) + + it('correctly clears start of the history', function() + -- Regression test: check absence of the memory leak when clearing start of + -- the history using ex_getln.c/clr_history(). + eq(1, funcs.histadd(':', 'foo')) + eq(1, funcs.histdel(':')) + eq('', funcs.histget(':', -1)) + end) + + it('correctly clears end of the history', function() + -- Regression test: check absence of the memory leak when clearing end of + -- the history using ex_getln.c/clr_history(). + meths.set_option('history', 1) + eq(1, funcs.histadd(':', 'foo')) + eq(1, funcs.histdel(':')) + eq('', funcs.histget(':', -1)) + end) + + it('correctly removes item from history', function() + -- Regression test: check that ex_getln.c/del_history_idx() correctly clears + -- history index after removing history entry. If it does not then deleting + -- history will result in a double free. + eq(1, funcs.histadd(':', 'foo')) + eq(1, funcs.histadd(':', 'bar')) + eq(1, funcs.histadd(':', 'baz')) + eq(1, funcs.histdel(':', -2)) + eq(1, funcs.histdel(':')) + eq('', funcs.histget(':', -1)) + end) +end) diff --git a/test/functional/editor/mode_insert_spec.lua b/test/functional/editor/mode_insert_spec.lua new file mode 100644 index 0000000000..46ab483036 --- /dev/null +++ b/test/functional/editor/mode_insert_spec.lua @@ -0,0 +1,89 @@ +-- Insert-mode tests. + +local helpers = require('test.functional.helpers')(after_each) +local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local expect = helpers.expect +local command = helpers.command +local eq = helpers.eq +local eval = helpers.eval +local meths = helpers.meths + +describe('insert-mode', function() + before_each(function() + clear() + end) + + it('CTRL-@', function() + -- Inserts last-inserted text, leaves insert-mode. + insert('hello') + feed('ix') + expect('hellhello') + + -- C-Space is the same as C-@. + -- CTRL-SPC inserts last-inserted text, leaves insert-mode. + feed('ix') + expect('hellhellhello') + + -- CTRL-A inserts last inserted text + feed('ix') + expect('hellhellhellhelloxo') + end) + + describe('Ctrl-R', function() + it('works', function() + command("let @@ = 'test'") + feed('i"') + expect('test') + end) + + it('works with multi-byte text', function() + command("let @@ = 'påskägg'") + feed('i"') + expect('påskägg') + end) + end) + + describe('Ctrl-O', function() + it('enters command mode for one command', function() + feed('ihello world') + feed(':let ctrlo = "test"') + feed('iii') + expect('hello worldiii') + eq(1, eval('ctrlo ==# "test"')) + end) + + it('re-enters insert mode at the end of the line when running startinsert', function() + -- #6962 + feed('ihello world') + feed(':startinsert') + feed('iii') + expect('hello worldiii') + end) + + it('re-enters insert mode at the beginning of the line when running startinsert', function() + insert('hello world') + feed('0') + feed(':startinsert') + feed('aaa') + expect('aaahello world') + end) + + it('re-enters insert mode in the middle of the line when running startinsert', function() + insert('hello world') + feed('bi') + feed(':startinsert') + feed('ooo') + expect('hello oooworld') + end) + + it("doesn't cancel Ctrl-O mode when processing event", function() + feed('iHello World') + eq({mode='niI', blocking=false}, meths.get_mode()) -- fast event + eq(2, eval('1+1')) -- causes K_EVENT key + eq({mode='niI', blocking=false}, meths.get_mode()) -- still in ctrl-o mode + feed('dd') + eq({mode='i', blocking=false}, meths.get_mode()) -- left ctrl-o mode + expect('') -- executed the command + end) + end) +end) diff --git a/test/functional/editor/mode_visual_spec.lua b/test/functional/editor/mode_visual_spec.lua new file mode 100644 index 0000000000..e9c117a1e5 --- /dev/null +++ b/test/functional/editor/mode_visual_spec.lua @@ -0,0 +1,26 @@ +-- Visual-mode tests. + +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval +local expect = helpers.expect +local feed = helpers.feed +local meths = helpers.meths + +describe('visual-mode', function() + before_each(clear) + + it("select-mode Ctrl-O doesn't cancel Ctrl-O mode when processing event #15688", function() + feed('iHello Worldgh') + eq({mode='vs', blocking=false}, meths.get_mode()) -- fast event + eq(2, eval('1+1')) -- causes K_EVENT key + eq({mode='vs', blocking=false}, meths.get_mode()) -- still in ctrl-o mode + feed('^') + eq({mode='s', blocking=false}, meths.get_mode()) -- left ctrl-o mode + feed('h') + eq({mode='i', blocking=false}, meths.get_mode()) -- entered insert mode + expect('h') -- selection is the whole line and is replaced + end) +end) + diff --git a/test/functional/editor/put_spec.lua b/test/functional/editor/put_spec.lua new file mode 100644 index 0000000000..26967ecbba --- /dev/null +++ b/test/functional/editor/put_spec.lua @@ -0,0 +1,939 @@ +local Screen = require('test.functional.ui.screen') +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local insert = helpers.insert +local feed = helpers.feed +local expect = helpers.expect +local eq = helpers.eq +local map = helpers.tbl_map +local filter = helpers.tbl_filter +local feed_command = helpers.feed_command +local curbuf_contents = helpers.curbuf_contents +local funcs = helpers.funcs +local dedent = helpers.dedent +local getreg = funcs.getreg + +local function reset() + feed_command('enew!') + insert([[ + Line of words 1 + Line of words 2]]) + feed_command('goto 1') + feed('itest_string.u') + funcs.setreg('a', 'test_stringa', 'V') + funcs.setreg('b', 'test_stringb\ntest_stringb\ntest_stringb', 'b') + funcs.setreg('"', 'test_string"', 'v') + feed_command('set virtualedit=') +end + +-- We check the last inserted register ". in each of these tests because it is +-- implemented completely differently in do_put(). +-- It is implemented differently so that control characters and imap'ped +-- characters work in the same manner when pasted as when inserted. +describe('put command', function() + clear() + before_each(reset) + + local function visual_marks_zero() + for _,v in pairs(funcs.getpos("'<")) do + if v ~= 0 then + return false + end + end + for _,v in pairs(funcs.getpos("'>")) do + if v ~= 0 then + return false + end + end + return true + end + + -- {{{ Where test definitions are run + local function run_test_variations(test_variations, extra_setup) + reset() + if extra_setup then extra_setup() end + local init_contents = curbuf_contents() + local init_cursorpos = funcs.getcurpos() + local assert_no_change = function (exception_table, after_undo) + expect(init_contents) + -- When putting the ". register forwards, undo doesn't move + -- the cursor back to where it was before. + -- This is because it uses the command character 'a' to + -- start the insert, and undo after that leaves the cursor + -- one place to the right (unless we were at the end of the + -- line when we pasted). + if not (exception_table.undo_position and after_undo) then + eq(funcs.getcurpos(), init_cursorpos) + end + end + + for _, test in pairs(test_variations) do + it(test.description, function() + if extra_setup then extra_setup() end + local orig_dotstr = funcs.getreg('.') + helpers.ok(visual_marks_zero()) + -- Make sure every test starts from the same conditions + assert_no_change(test.exception_table, false) + local was_cli = test.test_action() + test.test_assertions(test.exception_table, false) + -- Check that undo twice puts us back to the original conditions + -- (i.e. puts the cursor and text back to before) + feed('u') + assert_no_change(test.exception_table, true) + + -- Should not have changed the ". register + -- If we paste the ". register with a count we can't avoid + -- changing this register, hence avoid this check. + if not test.exception_table.dot_reg_changed then + eq(funcs.getreg('.'), orig_dotstr) + end + + -- Doing something, undoing it, and then redoing it should + -- leave us in the same state as just doing it once. + -- For :ex actions we want '@:', for normal actions we want '.' + + -- The '.' redo doesn't work for visual put so just exit if + -- it was tested. + -- We check that visual put was used by checking if the '< and + -- '> marks were changed. + if not visual_marks_zero() then + return + end + + if test.exception_table.undo_position then + funcs.setpos('.', init_cursorpos) + end + if was_cli then + feed('@:') + else + feed('.') + end + + test.test_assertions(test.exception_table, true) + end) + end + end -- run_test_variations() + -- }}} + + local function create_test_defs(test_defs, command_base, command_creator, -- {{{ + expect_base, expect_creator) + local rettab = {} + local exceptions + for _, v in pairs(test_defs) do + if v[4] then + exceptions = v[4] + else + exceptions = {} + end + table.insert(rettab, + { + test_action = command_creator(command_base, v[1]), + test_assertions = expect_creator(expect_base, v[2]), + description = v[3], + exception_table = exceptions, + }) + end + return rettab + end -- create_test_defs() }}} + + local function find_cursor_position(expect_string) -- {{{ + -- There must only be one occurance of the character 'x' in + -- expect_string. + -- This function removes that occurance, and returns the position that + -- it was in. + -- This returns the cursor position that would leave the 'x' in that + -- place if we feed 'ix' and the string existed before it. + for linenum, line in pairs(funcs.split(expect_string, '\n', 1)) do + local column = line:find('x') + if column then + return {linenum, column}, expect_string:gsub('x', '') + end + end + end -- find_cursor_position() }}} + + -- Action function creators {{{ + local function create_p_action(test_map, substitution) + local temp_val = test_map:gsub('p', substitution) + return function() + feed(temp_val) + return false + end + end + + local function create_put_action(command_base, substitution) + local temp_val = command_base:gsub('put', substitution) + return function() + feed_command(temp_val) + return true + end + end + -- }}} + + -- Expect function creator {{{ + local function expect_creator(conversion_function, expect_base, conversion_table) + local temp_expect_string = conversion_function(expect_base, conversion_table) + local cursor_position, expect_string = find_cursor_position(temp_expect_string) + return function(exception_table, after_redo) + expect(expect_string) + + -- Have to use getcurpos() instead of curwinmeths.get_cursor() in + -- order to account for virtualedit. + -- We always want the curswant element in getcurpos(), which is + -- sometimes different to the column element in + -- curwinmeths.get_cursor(). + -- NOTE: The ".gp command leaves the cursor after the pasted text + -- when running, but does not when the command is redone with the + -- '.' command. + if not (exception_table.redo_position and after_redo) then + local actual_position = funcs.getcurpos() + eq(cursor_position, {actual_position[2], actual_position[5]}) + end + end + end -- expect_creator() }}} + + -- Test definitions {{{ + local function copy_def(def) + local rettab = { '', {}, '', nil } + rettab[1] = def[1] + for k,v in pairs(def[2]) do + rettab[2][k] = v + end + rettab[3] = def[3] + if def[4] then + rettab[4] = {} + for k,v in pairs(def[4]) do + rettab[4][k] = v + end + end + return rettab + end + + local normal_command_defs = { + { + 'p', + {cursor_after = false, put_backwards = false, dot_register = false}, + 'pastes after cursor with p', + }, + { + 'gp', + {cursor_after = true, put_backwards = false, dot_register = false}, + 'leaves cursor after text with gp', + }, + { + '".p', + {cursor_after = false, put_backwards = false, dot_register = true}, + 'works with the ". register', + }, + { + '".gp', + {cursor_after = true, put_backwards = false, dot_register = true}, + 'gp works with the ". register', + {redo_position = true}, + }, + { + 'P', + {cursor_after = false, put_backwards = true, dot_register = false}, + 'pastes before cursor with P', + }, + { + 'gP', + {cursor_after = true, put_backwards = true, dot_register = false}, + 'gP pastes before cursor and leaves cursor after text', + }, + { + '".P', + {cursor_after = false, put_backwards = true, dot_register = true}, + 'P works with ". register', + }, + { + '".gP', + {cursor_after = true, put_backwards = true, dot_register = true}, + 'gP works with ". register', + {redo_position = true}, + }, + } + + -- Add a definition applying a count for each definition above. + -- Could do this for each transformation (p -> P, p -> gp etc), but I think + -- it's neater this way (balance between being explicit and too verbose). + for i = 1,#normal_command_defs do + local cur = normal_command_defs[i] + + -- Make modified copy of current definition that includes a count. + local newdef = copy_def(cur) + newdef[2].count = 2 + cur[2].count = 1 + newdef[1] = '2' .. newdef[1] + newdef[3] = 'double ' .. newdef[3] + + if cur[2].dot_register then + if not cur[4] then + newdef[4] = {} + end + newdef[4].dot_reg_changed = true + end + + normal_command_defs[#normal_command_defs + 1] = newdef + end + + local ex_command_defs = { + { + 'put', + {put_backwards = false, dot_register = false}, + 'pastes linewise forwards with :put', + }, + { + 'put!', + {put_backwards = true, dot_register = false}, + 'pastes linewise backwards with :put!', + }, + { + 'put .', + {put_backwards = false, dot_register = true}, + 'pastes linewise with the dot register', + }, + { + 'put! .', + {put_backwards = true, dot_register = true}, + 'pastes linewise backwards with the dot register', + }, + } + + local function non_dotdefs(def_table) + return filter(function(d) return not d[2].dot_register end, def_table) + end + + -- }}} + + -- Conversion functions {{{ + local function convert_charwise(expect_base, conversion_table, + virtualedit_end, visual_put) + expect_base = dedent(expect_base) + -- There is no difference between 'P' and 'p' when VIsual_active + if not visual_put then + if conversion_table.put_backwards then + -- Special case for virtualedit at the end of a line. + local replace_string + if not virtualedit_end then + replace_string = 'test_stringx"%1' + else + replace_string = 'test_stringx"' + end + expect_base = expect_base:gsub('(.)test_stringx"', replace_string) + end + end + if conversion_table.count > 1 then + local rep_string = 'test_string"' + local extra_puts = rep_string:rep(conversion_table.count - 1) + expect_base = expect_base:gsub('test_stringx"', extra_puts .. 'test_stringx"') + end + if conversion_table.cursor_after then + expect_base = expect_base:gsub('test_stringx"', 'test_string"x') + end + if conversion_table.dot_register then + expect_base = expect_base:gsub('(test_stringx?)"', '%1.') + end + return expect_base + end -- convert_charwise() + + local function make_back(string) + local prev_line + local rettab = {} + local string_found = false + for _, line in pairs(funcs.split(string, '\n', 1)) do + if line:find('test_string') then + string_found = true + table.insert(rettab, line) + else + if string_found then + if prev_line then + table.insert(rettab, prev_line) + prev_line = nil + end + table.insert(rettab, line) + else + table.insert(rettab, prev_line) + prev_line = line + end + end + end + -- In case there are no lines after the text that was put. + if prev_line and string_found then + table.insert(rettab, prev_line) + end + return table.concat(rettab, '\n') + end -- make_back() + + local function convert_linewise(expect_base, conversion_table, _, use_a, indent) + expect_base = dedent(expect_base) + if conversion_table.put_backwards then + expect_base = make_back(expect_base) + end + local p_str = 'test_string"' + if use_a then + p_str = 'test_stringa' + end + + if conversion_table.dot_register then + expect_base = expect_base:gsub('x' .. p_str, 'xtest_string.') + p_str = 'test_string.' + end + + if conversion_table.cursor_after then + expect_base = expect_base:gsub('x' .. p_str .. '\n', p_str .. '\nx') + end + + -- The 'indent' argument is only used here because a single put with an + -- indent doesn't require special handling. It doesn't require special + -- handling because the cursor is never put before the indent, hence + -- the modification of 'test_stringx"' gives the same overall answer as + -- modifying ' test_stringx"'. + + -- Only happens when using normal mode command actions. + if conversion_table.count and conversion_table.count > 1 then + if not indent then + indent = '' + end + local rep_string = indent .. p_str .. '\n' + local extra_puts = rep_string:rep(conversion_table.count - 1) + local orig_string, new_string + if conversion_table.cursor_after then + orig_string = indent .. p_str .. '\nx' + new_string = extra_puts .. orig_string + else + orig_string = indent .. 'x' .. p_str .. '\n' + new_string = orig_string .. extra_puts + end + expect_base = expect_base:gsub(orig_string, new_string) + end + return expect_base + end + + local function put_x_last(orig_line, p_str) + local prev_end, cur_end, cur_start = 0, 0, 0 + while cur_start do + prev_end = cur_end + cur_start, cur_end = orig_line:find(p_str, prev_end) + end + -- Assume (because that is the only way I call it) that p_str matches + -- the pattern 'test_string.' + return orig_line:sub(1, prev_end - 1) .. 'x' .. orig_line:sub(prev_end) + end + + local function convert_blockwise(expect_base, conversion_table, visual, + use_b, trailing_whitespace) + expect_base = dedent(expect_base) + local p_str = 'test_string"' + if use_b then + p_str = 'test_stringb' + end + + if conversion_table.dot_register then + expect_base = expect_base:gsub('(x?)' .. p_str, '%1test_string.') + -- Looks strange, but the dot is a special character in the pattern + -- and a literal character in the replacement. + expect_base = expect_base:gsub('test_stringx.', 'test_stringx.') + p_str = 'test_string.' + end + + -- No difference between 'p' and 'P' in visual mode. + if not visual then + if conversion_table.put_backwards then + -- One for the line where the cursor is left, one for all other + -- lines. + expect_base = expect_base:gsub('([^x])' .. p_str, p_str .. '%1') + expect_base = expect_base:gsub('([^x])x' .. p_str, 'x' .. p_str .. '%1') + if not trailing_whitespace then + expect_base = expect_base:gsub(' \n', '\n') + expect_base = expect_base:gsub(' $', '') + end + end + end + + if conversion_table.count and conversion_table.count > 1 then + local p_pattern = p_str:gsub('%.', '%%.') + expect_base = expect_base:gsub(p_pattern, + p_str:rep(conversion_table.count)) + expect_base = expect_base:gsub('test_stringx([b".])', + p_str:rep(conversion_table.count - 1) + .. '%0') + end + + if conversion_table.cursor_after then + if not visual then + local prev_line + local rettab = {} + local prev_in_block = false + for _, line in pairs(funcs.split(expect_base, '\n', 1)) do + if line:find('test_string') then + if prev_line then + prev_line = prev_line:gsub('x', '') + table.insert(rettab, prev_line) + end + prev_line = line + prev_in_block = true + else + if prev_in_block then + prev_line = put_x_last(prev_line, p_str) + table.insert(rettab, prev_line) + prev_in_block = false + end + table.insert(rettab, line) + end + end + if prev_line and prev_in_block then + table.insert(rettab, put_x_last(prev_line, p_str)) + end + + expect_base = table.concat(rettab, '\n') + else + expect_base = expect_base:gsub('x(.)', '%1x') + end + end + + return expect_base + end + -- }}} + + -- Convenience functions {{{ + local function run_normal_mode_tests(test_string, base_map, extra_setup, + virtualedit_end, selection_string) + local function convert_closure(e, c) + return convert_charwise(e, c, virtualedit_end, selection_string) + end + local function expect_normal_creator(expect_base, conversion_table) + local test_expect = expect_creator(convert_closure, expect_base, conversion_table) + return function(exception_table, after_redo) + test_expect(exception_table, after_redo) + if selection_string then + eq(getreg('"'), selection_string) + else + eq(getreg('"'), 'test_string"') + end + end + end + run_test_variations( + create_test_defs( + normal_command_defs, + base_map, + create_p_action, + test_string, + expect_normal_creator + ), + extra_setup + ) + end -- run_normal_mode_tests() + + local function convert_linewiseer(expect_base, conversion_table) + return expect_creator(convert_linewise, expect_base, conversion_table) + end + + local function run_linewise_tests(expect_base, base_command, extra_setup) + local linewise_test_defs = create_test_defs( + ex_command_defs, base_command, + create_put_action, expect_base, convert_linewiseer) + run_test_variations(linewise_test_defs, extra_setup) + end -- run_linewise_tests() + -- }}} + + -- Actual tests + describe('default pasting', function() + local expect_string = [[ + Ltest_stringx"ine of words 1 + Line of words 2]] + run_normal_mode_tests(expect_string, 'p') + + run_linewise_tests([[ + Line of words 1 + xtest_string" + Line of words 2]], + 'put' + ) + end) + + describe('linewise register', function() + -- put with 'p' + local local_ex_command_defs = non_dotdefs(normal_command_defs) + local base_expect_string = [[ + Line of words 1 + xtest_stringa + Line of words 2]] + local function local_convert_linewise(expect_base, conversion_table) + return convert_linewise(expect_base, conversion_table, nil, true) + end + local function expect_lineput(expect_base, conversion_table) + return expect_creator(local_convert_linewise, expect_base, conversion_table) + end + run_test_variations( + create_test_defs( + local_ex_command_defs, + '"ap', + create_p_action, + base_expect_string, + expect_lineput + ) + ) + + -- put with :put + local linewise_put_defs = non_dotdefs(ex_command_defs) + base_expect_string = [[ + Line of words 1 + xtest_stringa + Line of words 2]] + run_test_variations( + create_test_defs( + linewise_put_defs, + 'put a', create_put_action, + base_expect_string, convert_linewiseer + ) + ) + + end) + + describe('blockwise register', function() + local blockwise_put_defs = non_dotdefs(normal_command_defs) + local test_base = [[ + Lxtest_stringbine of words 1 + Ltest_stringbine of words 2 + test_stringb]] + + local function expect_block_creator(expect_base, conversion_table) + return expect_creator(function(e,c) return convert_blockwise(e,c,nil,true) end, + expect_base, conversion_table) + end + + run_test_variations( + create_test_defs( + blockwise_put_defs, + '"bp', + create_p_action, + test_base, + expect_block_creator + ) + ) + end) + + it('adds correct indentation when put with [p and ]p', function() + feed('G>>"a]pix') + -- luacheck: ignore + expect([[ + Line of words 1 + Line of words 2 + xtest_stringa]]) + feed('uu"a[pix') + -- luacheck: ignore + expect([[ + Line of words 1 + xtest_stringa + Line of words 2]]) + end) + + describe('linewise paste with autoindent', function() + -- luacheck: ignore + run_linewise_tests([[ + Line of words 1 + Line of words 2 + xtest_string"]], + 'put' + , + function() + funcs.setline('$', ' Line of words 2') + -- Set curswant to '8' to be at the end of the tab character + -- This is where the cursor is put back after the 'u' command. + funcs.setpos('.', {0, 2, 1, 0, 8}) + feed_command('set autoindent') + end + ) + end) + + describe('put inside tabs with virtualedit', function() + local test_string = [[ + Line of words 1 + test_stringx" Line of words 2]] + run_normal_mode_tests(test_string, 'p', function() + funcs.setline('$', ' Line of words 2') + feed_command('set virtualedit=all') + funcs.setpos('.', {0, 2, 1, 2, 3}) + end) + end) + + describe('put after the line with virtualedit', function() + -- luacheck: ignore 621 + local test_string = [[ + Line of words 1 test_stringx" + Line of words 2]] + run_normal_mode_tests(test_string, 'p', function() + funcs.setline('$', ' Line of words 2') + feed_command('set virtualedit=all') + funcs.setpos('.', {0, 1, 16, 1, 17}) + end, true) + end) + + describe('Visual put', function() + describe('basic put', function() + local test_string = [[ + test_stringx" words 1 + Line of words 2]] + run_normal_mode_tests(test_string, 'v2ep', nil, nil, 'Line of') + end) + describe('over trailing newline', function() + local test_string = 'Line of test_stringx"Line of words 2' + run_normal_mode_tests(test_string, 'v$p', function() + funcs.setpos('.', {0, 1, 9, 0, 9}) + end, + nil, + 'words 1\n') + end) + describe('linewise mode', function() + local test_string = [[ + xtest_string" + Line of words 2]] + local function expect_vis_linewise(expect_base, conversion_table) + return expect_creator(function(e, c) + return convert_linewise(e, c, nil, nil) + end, + expect_base, conversion_table) + end + run_test_variations( + create_test_defs( + normal_command_defs, + 'Vp', + create_p_action, + test_string, + expect_vis_linewise + ), + function() funcs.setpos('.', {0, 1, 1, 0, 1}) end + ) + + describe('with whitespace at bol', function() + local function expect_vis_lineindented(expect_base, conversion_table) + local test_expect = expect_creator(function(e, c) + return convert_linewise(e, c, nil, nil, ' ') + end, + expect_base, conversion_table) + return function(exception_table, after_redo) + test_expect(exception_table, after_redo) + eq(getreg('"'), 'Line of words 1\n') + end + end + local base_expect_string = [[ + xtest_string" + Line of words 2]] + run_test_variations( + create_test_defs( + normal_command_defs, + 'Vp', + create_p_action, + base_expect_string, + expect_vis_lineindented + ), + function() + feed('i test_string.u') + funcs.setreg('"', ' test_string"', 'v') + end + ) + end) + + end) + + describe('blockwise visual mode', function() + local test_base = [[ + test_stringx"e of words 1 + test_string"e of words 2]] + + local function expect_block_creator(expect_base, conversion_table) + local test_expect = expect_creator(function(e, c) + return convert_blockwise(e, c, true) + end, expect_base, conversion_table) + return function(e,c) + test_expect(e,c) + eq(getreg('"'), 'Lin\nLin') + end + end + + local select_down_test_defs = create_test_defs( + normal_command_defs, + 'jllp', + create_p_action, + test_base, + expect_block_creator + ) + run_test_variations(select_down_test_defs) + + + -- Undo and redo of a visual block put leave the cursor in the top + -- left of the visual block area no matter where the cursor was + -- when it started. + local undo_redo_no = map(function(table) + local rettab = copy_def(table) + if not rettab[4] then + rettab[4] = {} + end + rettab[4].undo_position = true + rettab[4].redo_position = true + return rettab + end, + normal_command_defs) + + -- Selection direction doesn't matter + run_test_variations( + create_test_defs( + undo_redo_no, + 'kllp', + create_p_action, + test_base, + expect_block_creator + ), + function() funcs.setpos('.', {0, 2, 1, 0, 1}) end + ) + + describe('blockwise cursor after undo', function() + -- A bit of a hack of the reset above. + -- In the tests that selection direction doesn't matter, we + -- don't check the undo/redo position because it doesn't fit + -- the same pattern as everything else. + -- Here we fix this by directly checking the undo/redo position + -- in the test_assertions of our test definitions. + local function assertion_creator(_,_) + return function(_,_) + feed('u') + -- Have to use feed('u') here to set curswant, because + -- ex_undo() doesn't do that. + eq(funcs.getcurpos(), {0, 1, 1, 0, 1}) + feed('') + eq(funcs.getcurpos(), {0, 1, 1, 0, 1}) + end + end + + run_test_variations( + create_test_defs( + undo_redo_no, + 'kllp', + create_p_action, + test_base, + assertion_creator + ), + function() funcs.setpos('.', {0, 2, 1, 0, 1}) end + ) + end) + end) + + + describe("with 'virtualedit'", function() + describe('splitting a tab character', function() + local base_expect_string = [[ + Line of words 1 + test_stringx" Line of words 2]] + run_normal_mode_tests( + base_expect_string, + 'vp', + function() + funcs.setline('$', ' Line of words 2') + feed_command('set virtualedit=all') + funcs.setpos('.', {0, 2, 1, 2, 3}) + end, + nil, + ' ' + ) + end) + describe('after end of line', function() + local base_expect_string = [[ + Line of words 1 test_stringx" + Line of words 2]] + run_normal_mode_tests( + base_expect_string, + 'vp', + function() + feed_command('set virtualedit=all') + funcs.setpos('.', {0, 1, 16, 2, 18}) + end, + true, + ' ' + ) + end) + end) + end) + + describe('. register special tests', function() + -- luacheck: ignore 621 + before_each(reset) + it('applies control character actions', function() + feed('iu') + expect([[ + Line of words 1 + Line of words 2]]) + feed('".p') + expect([[ + Line of words 1 + Line of words 2]]) + feed('u1goj".p') + eq([[ + ine of words 1 + ine of words 2]], curbuf_contents()) + end) + + local function bell_test(actions, should_ring) + local screen = Screen.new() + screen:attach() + if should_ring then + -- check bell is not set by nvim before the action + screen:sleep(50) + end + helpers.ok(not screen.bell and not screen.visualbell) + actions() + screen:expect{condition=function() + if should_ring then + if not screen.bell and not screen.visualbell then + error('Bell was not rung after action') + end + else + if screen.bell or screen.visualbell then + error('Bell was rung after action') + end + end + end, unchanged=(not should_ring)} + screen:detach() + end + + it('should not ring the bell with gp at end of line', function() + bell_test(function() feed('$".gp') end) + + -- Even if the last character is a multibyte character. + reset() + funcs.setline(1, 'helloม') + bell_test(function() feed('$".gp') end) + end) + + it('should not ring the bell with gp and end of file', function() + funcs.setpos('.', {0, 2, 1, 0}) + bell_test(function() feed('$vl".gp') end) + end) + + it('should ring the bell when deleting if not appropriate', function() + feed_command('goto 2') + feed('i') + expect([[ + ine of words 1 + Line of words 2]]) + bell_test(function() feed('".P') end, true) + end) + + it('should restore cursor position after undo of ".p', function() + local origpos = funcs.getcurpos() + feed('".pu') + eq(origpos, funcs.getcurpos()) + end) + + it("should be unaffected by 'autoindent' with V\".2p", function() + feed_command('set autoindent') + feed('i test_string.u') + feed('V".2p') + expect([[ + test_string. + test_string. + Line of words 2]]) + end) + end) +end) + diff --git a/test/functional/editor/search_spec.lua b/test/functional/editor/search_spec.lua new file mode 100644 index 0000000000..d5df131725 --- /dev/null +++ b/test/functional/editor/search_spec.lua @@ -0,0 +1,17 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq +local pcall_err = helpers.pcall_err + +describe('search (/)', function() + before_each(clear) + + it('fails with huge column (%c) value #9930', function() + eq([[Vim:E951: \% value too large]], + pcall_err(command, "/\\v%18446744071562067968c")) + eq([[Vim:E951: \% value too large]], + pcall_err(command, "/\\v%2147483648c")) + end) +end) + diff --git a/test/functional/editor/tabpage_spec.lua b/test/functional/editor/tabpage_spec.lua new file mode 100644 index 0000000000..d1d6854b07 --- /dev/null +++ b/test/functional/editor/tabpage_spec.lua @@ -0,0 +1,38 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq +local feed = helpers.feed +local eval = helpers.eval + +describe('tabpage', function() + before_each(clear) + + it('advances to the next page via gt', function() + -- add some tabpages + command('tabnew') + command('tabnew') + command('tabnew') + + eq(4, eval('tabpagenr()')) + + feed('gt') + + eq(1, eval('tabpagenr()')) + end) + + it('retreats to the previous page via gT', function() + -- add some tabpages + command('tabnew') + command('tabnew') + command('tabnew') + + eq(4, eval('tabpagenr()')) + + feed('gT') + + eq(3, eval('tabpagenr()')) + end) +end) + diff --git a/test/functional/editor/undo_spec.lua b/test/functional/editor/undo_spec.lua new file mode 100644 index 0000000000..a023ca3d90 --- /dev/null +++ b/test/functional/editor/undo_spec.lua @@ -0,0 +1,61 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local command = helpers.command +local expect = helpers.expect +local feed = helpers.feed +local insert = helpers.insert + +describe('u CTRL-R g- g+', function() + before_each(clear) + + local function create_history(num_steps) + if num_steps == 0 then return end + insert('1') + if num_steps == 1 then return end + feed('o2') + feed('o3') + feed('u') + if num_steps == 2 then return end + feed('o4') + if num_steps == 3 then return end + feed('u') + end + + local function undo_and_redo(hist_pos, undo, redo, expect_str) + command('enew!') + create_history(hist_pos) + local cur_contents = helpers.curbuf_contents() + feed(undo) + expect(expect_str) + feed(redo) + expect(cur_contents) + end + + -- TODO Look for message saying 'Already at oldest change' + it('does nothing when no changes have happened', function() + undo_and_redo(0, 'u', '', '') + undo_and_redo(0, 'g-', 'g+', '') + end) + it('undoes a change when at a leaf', function() + undo_and_redo(1, 'u', '', '') + undo_and_redo(1, 'g-', 'g+', '') + end) + it('undoes a change when in a non-leaf', function() + undo_and_redo(2, 'u', '', '1') + undo_and_redo(2, 'g-', 'g+', '1') + end) + it('undoes properly around a branch point', function() + undo_and_redo(3, 'u', '', [[ + 1 + 2]]) + undo_and_redo(3, 'g-', 'g+', [[ + 1 + 2 + 3]]) + end) + it('can find the previous sequence after undoing to a branch', function() + undo_and_redo(4, 'u', '', '1') + undo_and_redo(4, 'g-', 'g+', '1') + end) +end) diff --git a/test/functional/eval/api_functions_spec.lua b/test/functional/eval/api_functions_spec.lua deleted file mode 100644 index d07e74d40e..0000000000 --- a/test/functional/eval/api_functions_spec.lua +++ /dev/null @@ -1,167 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local Screen = require('test.functional.ui.screen') -local lfs = require('lfs') -local neq, eq, command = helpers.neq, helpers.eq, helpers.command -local clear, curbufmeths = helpers.clear, helpers.curbufmeths -local exc_exec, expect, eval = helpers.exc_exec, helpers.expect, helpers.eval -local insert, pcall_err = helpers.insert, helpers.pcall_err -local meths = helpers.meths - -describe('eval-API', function() - before_each(clear) - - it("work", function() - command("call nvim_command('let g:test = 1')") - eq(1, eval("nvim_get_var('test')")) - - local buf = eval("nvim_get_current_buf()") - command("call nvim_buf_set_lines("..buf..", 0, -1, v:true, ['aa', 'bb'])") - expect([[ - aa - bb]]) - - command("call nvim_win_set_cursor(0, [1, 1])") - command("call nvim_input('ax')") - expect([[ - aax - bb]]) - end) - - it("throw errors for invalid arguments", function() - local err = exc_exec('call nvim_get_current_buf("foo")') - eq('Vim(call):E118: Too many arguments for function: nvim_get_current_buf', err) - - err = exc_exec('call nvim_set_option("hlsearch")') - eq('Vim(call):E119: Not enough arguments for function: nvim_set_option', err) - - err = exc_exec('call nvim_buf_set_lines(1, 0, -1, [], ["list"])') - eq('Vim(call):E5555: API call: Wrong type for argument 4 when calling nvim_buf_set_lines, expecting Boolean', err) - - err = exc_exec('call nvim_buf_set_lines(0, 0, -1, v:true, "string")') - eq('Vim(call):E5555: API call: Wrong type for argument 5 when calling nvim_buf_set_lines, expecting ArrayOf(String)', err) - - err = exc_exec('call nvim_buf_get_number("0")') - eq('Vim(call):E5555: API call: Wrong type for argument 1 when calling nvim_buf_get_number, expecting Buffer', err) - - err = exc_exec('call nvim_buf_line_count(17)') - eq('Vim(call):E5555: API call: Invalid buffer id: 17', err) - end) - - it('cannot change texts if textlocked', function() - command("autocmd TextYankPost ++once call nvim_buf_set_lines(0, 0, -1, v:false, [])") - eq('Vim(call):E5555: API call: E523: Not allowed here', pcall_err(command, "normal! yy")) - end) - - it("use buffer numbers and windows ids as handles", function() - local screen = Screen.new(40, 8) - screen:attach() - local bnr = eval("bufnr('')") - local bhnd = eval("nvim_get_current_buf()") - local wid = eval("win_getid()") - local whnd = eval("nvim_get_current_win()") - eq(bnr, bhnd) - eq(wid, whnd) - - command("new") -- creates new buffer and new window - local bnr2 = eval("bufnr('')") - local bhnd2 = eval("nvim_get_current_buf()") - local wid2 = eval("win_getid()") - local whnd2 = eval("nvim_get_current_win()") - eq(bnr2, bhnd2) - eq(wid2, whnd2) - neq(bnr, bnr2) - neq(wid, wid2) - -- 0 is synonymous to the current buffer - eq(bnr2, eval("nvim_buf_get_number(0)")) - - command("bn") -- show old buffer in new window - eq(bnr, eval("nvim_get_current_buf()")) - eq(bnr, eval("bufnr('')")) - eq(bnr, eval("nvim_buf_get_number(0)")) - eq(wid2, eval("win_getid()")) - eq(whnd2, eval("nvim_get_current_win()")) - end) - - it("get_lines and set_lines use NL to represent NUL", function() - curbufmeths.set_lines(0, -1, true, {"aa\0", "b\0b"}) - eq({'aa\n', 'b\nb'}, eval("nvim_buf_get_lines(0, 0, -1, 1)")) - - command('call nvim_buf_set_lines(0, 1, 2, v:true, ["xx", "\\nyy"])') - eq({'aa\0', 'xx', '\0yy'}, curbufmeths.get_lines(0, -1, 1)) - end) - - it("that are FUNC_ATTR_NOEVAL cannot be called", function() - -- Deprecated vim_ prefix is not exported. - local err = exc_exec('call vim_get_current_buffer("foo")') - eq('Vim(call):E117: Unknown function: vim_get_current_buffer', err) - - -- Deprecated buffer_ prefix is not exported. - err = exc_exec('call buffer_line_count(0)') - eq('Vim(call):E117: Unknown function: buffer_line_count', err) - - -- Functions deprecated before the api functions became available - -- in vimscript are not exported. - err = exc_exec('call buffer_get_line(0, 1)') - eq('Vim(call):E117: Unknown function: buffer_get_line', err) - - -- some api functions are only useful from a msgpack-rpc channel - err = exc_exec('call nvim_subscribe("fancyevent")') - eq('Vim(call):E117: Unknown function: nvim_subscribe', err) - end) - - it('have metadata accessible with api_info()', function() - local api_keys = eval("sort(keys(api_info()))") - eq({'error_types', 'functions', 'types', - 'ui_events', 'ui_options', 'version'}, api_keys) - end) - - it('are highlighted by vim.vim syntax file', function() - if lfs.attributes("build/runtime/syntax/vim/generated.vim",'uid') == nil then - pending("runtime was not built, skipping test") - return - end - local screen = Screen.new(40, 8) - screen:attach() - screen:set_default_attr_ids({ - [1] = {bold = true, foreground = Screen.colors.Brown}, - [2] = {foreground = Screen.colors.DarkCyan}, - [3] = {foreground = Screen.colors.SlateBlue}, - [4] = {foreground = Screen.colors.Fuchsia}, - [5] = {bold = true, foreground = Screen.colors.Blue}, - }) - - command("set ft=vim") - command("let &rtp='build/runtime/,'.&rtp") - command("syntax on") - insert([[ - call bufnr('%') - call nvim_input('typing...') - call not_a_function(42)]]) - - screen:expect([[ - {1:call} {2:bufnr}{3:(}{4:'%'}{3:)} | - {1:call} {2:nvim_input}{3:(}{4:'typing...'}{3:)} | - {1:call} not_a_function{3:(}{4:42}{3:^)} | - {5:~ }| - {5:~ }| - {5:~ }| - {5:~ }| - | - ]]) - end) - - it('cannot be called from sandbox', function() - eq('Vim(call):E48: Not allowed in sandbox', - pcall_err(command, "sandbox call nvim_input('ievil')")) - eq({''}, meths.buf_get_lines(0, 0, -1, true)) - end) - - it('converts blobs to API strings', function() - command('let g:v1 = nvim__id(0z68656c6c6f)') - command('let g:v2 = nvim__id(v:_null_blob)') - eq(1, eval('type(g:v1)')) - eq(1, eval('type(g:v2)')) - eq('hello', eval('g:v1')) - eq('', eval('g:v2')) - end) -end) diff --git a/test/functional/eval/backtick_expansion_spec.lua b/test/functional/eval/backtick_expansion_spec.lua deleted file mode 100644 index b1b44cfa8b..0000000000 --- a/test/functional/eval/backtick_expansion_spec.lua +++ /dev/null @@ -1,50 +0,0 @@ -local lfs = require('lfs') -local helpers = require('test.functional.helpers')(after_each) -local clear, command, eval, eq = helpers.clear, helpers.command, helpers.eval, helpers.eq -local write_file = helpers.write_file - -describe("backtick expansion", function() - setup(function() - clear() - lfs.mkdir("test-backticks") - write_file("test-backticks/file1", "test file 1") - write_file("test-backticks/file2", "test file 2") - write_file("test-backticks/file3", "test file 3") - lfs.mkdir("test-backticks/subdir") - write_file("test-backticks/subdir/file4", "test file 4") - -- Long path might cause "Press ENTER" prompt; use :silent to avoid it. - command('silent cd test-backticks') - end) - - teardown(function() - helpers.rmdir('test-backticks') - end) - - it("with default 'shell'", function() - if helpers.iswin() then - command(":silent args `dir /b *2`") - else - command(":silent args `echo ***2`") - end - eq({ "file2", }, eval("argv()")) - if helpers.iswin() then - command(":silent args `dir /s/b *4`") - eq({ "subdir\\file4", }, eval("map(argv(), 'fnamemodify(v:val, \":.\")')")) - else - command(":silent args `echo */*4`") - eq({ "subdir/file4", }, eval("argv()")) - end - end) - - it("with shell=fish", function() - if eval("executable('fish')") == 0 then - pending('missing "fish" command') - return - end - command("set shell=fish") - command(":silent args `echo ***2`") - eq({ "file2", }, eval("argv()")) - command(":silent args `echo */*4`") - eq({ "subdir/file4", }, eval("argv()")) - end) -end) diff --git a/test/functional/eval/buf_functions_spec.lua b/test/functional/eval/buf_functions_spec.lua deleted file mode 100644 index e957e5f5af..0000000000 --- a/test/functional/eval/buf_functions_spec.lua +++ /dev/null @@ -1,306 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local lfs = require('lfs') - -local eq = helpers.eq -local clear = helpers.clear -local funcs = helpers.funcs -local meths = helpers.meths -local command = helpers.command -local exc_exec = helpers.exc_exec -local bufmeths = helpers.bufmeths -local winmeths = helpers.winmeths -local curbufmeths = helpers.curbufmeths -local curwinmeths = helpers.curwinmeths -local curtabmeths = helpers.curtabmeths -local get_pathsep = helpers.get_pathsep -local rmdir = helpers.rmdir -local pcall_err = helpers.pcall_err - -local fname = 'Xtest-functional-eval-buf_functions' -local fname2 = fname .. '.2' -local dirname = fname .. '.d' - -before_each(clear) - -for _, func in ipairs({'bufname(%s)', 'bufnr(%s)', 'bufwinnr(%s)', - 'getbufline(%s, 1)', 'getbufvar(%s, "changedtick")', - 'setbufvar(%s, "f", 0)'}) do - local funcname = func:match('%w+') - describe(funcname .. '() function', function() - it('errors out when receives v:true/v:false/v:null', function() - -- Not compatible with Vim: in Vim it always results in buffer not found - -- without any error messages. - for _, var in ipairs({'v:true', 'v:false'}) do - eq('Vim(call):E5299: Expected a Number or a String, Boolean found', - exc_exec('call ' .. func:format(var))) - end - eq('Vim(call):E5300: Expected a Number or a String', - exc_exec('call ' .. func:format('v:null'))) - end) - it('errors out when receives invalid argument', function() - eq('Vim(call):E745: Expected a Number or a String, List found', - exc_exec('call ' .. func:format('[]'))) - eq('Vim(call):E728: Expected a Number or a String, Dictionary found', - exc_exec('call ' .. func:format('{}'))) - eq('Vim(call):E805: Expected a Number or a String, Float found', - exc_exec('call ' .. func:format('0.0'))) - eq('Vim(call):E703: Expected a Number or a String, Funcref found', - exc_exec('call ' .. func:format('function("tr")'))) - end) - end) -end - -describe('bufname() function', function() - it('returns empty string when buffer was not found', function() - command('file ' .. fname) - eq('', funcs.bufname(2)) - eq('', funcs.bufname('non-existent-buffer')) - eq('', funcs.bufname('#')) - command('edit ' .. fname2) - eq(2, funcs.bufnr('%')) - eq('', funcs.bufname('X')) - end) - before_each(function() - lfs.mkdir(dirname) - end) - after_each(function() - rmdir(dirname) - end) - it('returns expected buffer name', function() - eq('', funcs.bufname('%')) -- Buffer has no name yet - command('file ' .. fname) - local wd = lfs.currentdir() - local sep = get_pathsep() - local curdirname = funcs.fnamemodify(wd, ':t') - for _, arg in ipairs({'%', 1, 'X', wd}) do - eq(fname, funcs.bufname(arg)) - meths.set_current_dir('..') - eq(curdirname .. sep .. fname, funcs.bufname(arg)) - meths.set_current_dir(curdirname) - meths.set_current_dir(dirname) - eq(wd .. sep .. fname, funcs.bufname(arg)) - meths.set_current_dir('..') - eq(fname, funcs.bufname(arg)) - command('enew') - end - eq('', funcs.bufname('%')) - eq('', funcs.bufname('$')) - eq(2, funcs.bufnr('%')) - end) -end) - -describe('bufnr() function', function() - it('returns -1 when buffer was not found', function() - command('file ' .. fname) - eq(-1, funcs.bufnr(2)) - eq(-1, funcs.bufnr('non-existent-buffer')) - eq(-1, funcs.bufnr('#')) - command('edit ' .. fname2) - eq(2, funcs.bufnr('%')) - eq(-1, funcs.bufnr('X')) - end) - it('returns expected buffer number', function() - eq(1, funcs.bufnr('%')) - command('file ' .. fname) - local wd = lfs.currentdir() - local curdirname = funcs.fnamemodify(wd, ':t') - eq(1, funcs.bufnr(fname)) - eq(1, funcs.bufnr(wd)) - eq(1, funcs.bufnr(curdirname)) - eq(1, funcs.bufnr('X')) - end) - it('returns number of last buffer with "$"', function() - eq(1, funcs.bufnr('$')) - command('new') - eq(2, funcs.bufnr('$')) - command('new') - eq(3, funcs.bufnr('$')) - command('only') - eq(3, funcs.bufnr('$')) - eq(3, funcs.bufnr('%')) - command('buffer 1') - eq(3, funcs.bufnr('$')) - eq(1, funcs.bufnr('%')) - command('bwipeout 2') - eq(3, funcs.bufnr('$')) - eq(1, funcs.bufnr('%')) - command('bwipeout 3') - eq(1, funcs.bufnr('$')) - eq(1, funcs.bufnr('%')) - command('new') - eq(4, funcs.bufnr('$')) - end) -end) - -describe('bufwinnr() function', function() - it('returns -1 when buffer was not found', function() - command('file ' .. fname) - eq(-1, funcs.bufwinnr(2)) - eq(-1, funcs.bufwinnr('non-existent-buffer')) - eq(-1, funcs.bufwinnr('#')) - command('split ' .. fname2) -- It would be OK if there was one window - eq(2, funcs.bufnr('%')) - eq(-1, funcs.bufwinnr('X')) - end) - before_each(function() - lfs.mkdir(dirname) - end) - after_each(function() - rmdir(dirname) - end) - it('returns expected window number', function() - eq(1, funcs.bufwinnr('%')) - command('file ' .. fname) - command('vsplit') - command('split ' .. fname2) - eq(2, funcs.bufwinnr(fname)) - eq(1, funcs.bufwinnr(fname2)) - eq(-1, funcs.bufwinnr(fname:sub(1, #fname - 1))) - meths.set_current_dir(dirname) - eq(2, funcs.bufwinnr(fname)) - eq(1, funcs.bufwinnr(fname2)) - eq(-1, funcs.bufwinnr(fname:sub(1, #fname - 1))) - eq(1, funcs.bufwinnr('%')) - eq(2, funcs.bufwinnr(1)) - eq(1, funcs.bufwinnr(2)) - eq(-1, funcs.bufwinnr(3)) - eq(1, funcs.bufwinnr('$')) - end) -end) - -describe('getbufline() function', function() - it('returns empty list when buffer was not found', function() - command('file ' .. fname) - eq({}, funcs.getbufline(2, 1)) - eq({}, funcs.getbufline('non-existent-buffer', 1)) - eq({}, funcs.getbufline('#', 1)) - command('edit ' .. fname2) - eq(2, funcs.bufnr('%')) - eq({}, funcs.getbufline('X', 1)) - end) - it('returns empty list when range is invalid', function() - eq({}, funcs.getbufline(1, 0)) - curbufmeths.set_lines(0, 1, false, {'foo', 'bar', 'baz'}) - eq({}, funcs.getbufline(1, 2, 1)) - eq({}, funcs.getbufline(1, -10, -20)) - eq({}, funcs.getbufline(1, -2, -1)) - eq({}, funcs.getbufline(1, -1, 9999)) - end) - it('returns expected lines', function() - meths.set_option('hidden', true) - command('file ' .. fname) - curbufmeths.set_lines(0, 1, false, {'foo\0', '\0bar', 'baz'}) - command('edit ' .. fname2) - curbufmeths.set_lines(0, 1, false, {'abc\0', '\0def', 'ghi'}) - eq({'foo\n', '\nbar', 'baz'}, funcs.getbufline(1, 1, 9999)) - eq({'abc\n', '\ndef', 'ghi'}, funcs.getbufline(2, 1, 9999)) - eq({'foo\n', '\nbar', 'baz'}, funcs.getbufline(1, 1, '$')) - eq({'baz'}, funcs.getbufline(1, '$', '$')) - eq({'baz'}, funcs.getbufline(1, '$', 9999)) - end) -end) - -describe('getbufvar() function', function() - it('returns empty list when buffer was not found', function() - command('file ' .. fname) - eq('', funcs.getbufvar(2, '&autoindent')) - eq('', funcs.getbufvar('non-existent-buffer', '&autoindent')) - eq('', funcs.getbufvar('#', '&autoindent')) - command('edit ' .. fname2) - eq(2, funcs.bufnr('%')) - eq('', funcs.getbufvar('X', '&autoindent')) - end) - it('returns empty list when variable/option/etc was not found', function() - command('file ' .. fname) - eq('', funcs.getbufvar(1, '&autondent')) - eq('', funcs.getbufvar(1, 'changedtic')) - end) - it('returns expected option value', function() - eq(0, funcs.getbufvar(1, '&autoindent')) - eq(0, funcs.getbufvar(1, '&l:autoindent')) - eq(0, funcs.getbufvar(1, '&g:autoindent')) - -- Also works with global-only options - eq(1, funcs.getbufvar(1, '&hidden')) - eq(1, funcs.getbufvar(1, '&l:hidden')) - eq(1, funcs.getbufvar(1, '&g:hidden')) - -- Also works with window-local options - eq(0, funcs.getbufvar(1, '&number')) - eq(0, funcs.getbufvar(1, '&l:number')) - eq(0, funcs.getbufvar(1, '&g:number')) - command('new') - -- But with window-local options it probably does not what you expect - command("setl number") - -- (note that current window’s buffer is 2, but getbufvar() receives 1) - eq({id=2}, curwinmeths.get_buf()) - eq(1, funcs.getbufvar(1, '&number')) - eq(1, funcs.getbufvar(1, '&l:number')) - -- You can get global value though, if you find this useful. - eq(0, funcs.getbufvar(1, '&g:number')) - end) - it('returns expected variable value', function() - eq(2, funcs.getbufvar(1, 'changedtick')) - curbufmeths.set_lines(0, 1, false, {'abc\0', '\0def', 'ghi'}) - eq(3, funcs.getbufvar(1, 'changedtick')) - curbufmeths.set_var('test', true) - eq(true, funcs.getbufvar(1, 'test')) - eq({test=true, changedtick=3}, funcs.getbufvar(1, '')) - command('new') - eq(3, funcs.getbufvar(1, 'changedtick')) - eq(true, funcs.getbufvar(1, 'test')) - eq({test=true, changedtick=3}, funcs.getbufvar(1, '')) - end) -end) - -describe('setbufvar() function', function() - it('throws the error or ignores the input when buffer was not found', function() - command('file ' .. fname) - eq(0, - exc_exec('call setbufvar(2, "&autoindent", 0)')) - eq('Vim(call):E94: No matching buffer for non-existent-buffer', - exc_exec('call setbufvar("non-existent-buffer", "&autoindent", 0)')) - eq(0, - exc_exec('call setbufvar("#", "&autoindent", 0)')) - command('edit ' .. fname2) - eq(2, funcs.bufnr('%')) - eq('Vim(call):E93: More than one match for X', - exc_exec('call setbufvar("X", "&autoindent", 0)')) - end) - it('may set options, including window-local and global values', function() - local buf1 = meths.get_current_buf() - eq(false, curwinmeths.get_option('number')) - command('split') - command('new') - eq(2, bufmeths.get_number(curwinmeths.get_buf())) - funcs.setbufvar(1, '&number', true) - local windows = curtabmeths.list_wins() - eq(false, winmeths.get_option(windows[1], 'number')) - eq(true, winmeths.get_option(windows[2], 'number')) - eq(false, winmeths.get_option(windows[3], 'number')) - eq(false, winmeths.get_option(meths.get_current_win(), 'number')) - - eq(true, meths.get_option('hidden')) - funcs.setbufvar(1, '&hidden', 0) - eq(false, meths.get_option('hidden')) - - eq(false, bufmeths.get_option(buf1, 'autoindent')) - funcs.setbufvar(1, '&autoindent', true) - eq(true, bufmeths.get_option(buf1, 'autoindent')) - eq('Vim(call):E355: Unknown option: xxx', - exc_exec('call setbufvar(1, "&xxx", 0)')) - end) - it('may set variables', function() - local buf1 = meths.get_current_buf() - command('split') - command('new') - eq(2, curbufmeths.get_number()) - funcs.setbufvar(1, 'number', true) - eq(true, bufmeths.get_var(buf1, 'number')) - eq('Vim(call):E461: Illegal variable name: b:', - exc_exec('call setbufvar(1, "", 0)')) - eq(true, bufmeths.get_var(buf1, 'number')) - eq('Vim:E46: Cannot change read-only variable "b:changedtick"', - pcall_err(funcs.setbufvar, 1, 'changedtick', true)) - eq(2, funcs.getbufvar(1, 'changedtick')) - end) -end) diff --git a/test/functional/eval/changedtick_spec.lua b/test/functional/eval/changedtick_spec.lua deleted file mode 100644 index 99406d9d7a..0000000000 --- a/test/functional/eval/changedtick_spec.lua +++ /dev/null @@ -1,142 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed -local clear = helpers.clear -local funcs = helpers.funcs -local meths = helpers.meths -local command = helpers.command -local exc_exec = helpers.exc_exec -local redir_exec = helpers.redir_exec -local pcall_err = helpers.pcall_err -local curbufmeths = helpers.curbufmeths - -before_each(clear) - -local function changedtick() - local ct = curbufmeths.get_changedtick() - eq(ct, curbufmeths.get_var('changedtick')) - eq(ct, curbufmeths.get_var('changedtick')) - eq(ct, eval('b:changedtick')) - eq(ct, eval('b:["changedtick"]')) - eq(ct, eval('b:.changedtick')) - eq(ct, funcs.getbufvar('%', 'changedtick')) - eq(ct, funcs.getbufvar('%', '').changedtick) - eq(ct, eval('b:').changedtick) - return ct -end - -describe('b:changedtick', function() - -- Ported tests from Vim-8.0.333 - it('increments', function() -- Test_changedtick_increments - -- New buffer has an empty line, tick starts at 2 - eq(2, changedtick()) - funcs.setline(1, 'hello') - eq(3, changedtick()) - eq(0, exc_exec('undo')) - -- Somehow undo counts as two changes - eq(5, changedtick()) - end) - it('is present in b: dictionary', function() - eq(2, changedtick()) - command('let d = b:') - eq(2, meths.get_var('d').changedtick) - end) - it('increments at bdel', function() - command('new') - eq(2, changedtick()) - local bnr = curbufmeths.get_number() - eq(2, bnr) - command('bdel') - eq(3, funcs.getbufvar(bnr, 'changedtick')) - eq(1, curbufmeths.get_number()) - end) - it('fails to be changed by user', function() - local ct = changedtick() - local ctn = ct + 100500 - eq(0, exc_exec('let d = b:')) - eq('\nE46: Cannot change read-only variable "b:changedtick"', - redir_exec('let b:changedtick = ' .. ctn)) - eq('\nE46: Cannot change read-only variable "b:["changedtick"]"', - redir_exec('let b:["changedtick"] = ' .. ctn)) - eq('\nE46: Cannot change read-only variable "b:.changedtick"', - redir_exec('let b:.changedtick = ' .. ctn)) - eq('\nE46: Cannot change read-only variable "d.changedtick"', - redir_exec('let d.changedtick = ' .. ctn)) - eq('Key is read-only: changedtick', - pcall_err(curbufmeths.set_var, 'changedtick', ctn)) - - eq('\nE795: Cannot delete variable b:changedtick', - redir_exec('unlet b:changedtick')) - eq('\nE46: Cannot change read-only variable "b:.changedtick"', - redir_exec('unlet b:.changedtick')) - eq('\nE46: Cannot change read-only variable "b:["changedtick"]"', - redir_exec('unlet b:["changedtick"]')) - eq('\nE46: Cannot change read-only variable "d.changedtick"', - redir_exec('unlet d.changedtick')) - eq('Key is read-only: changedtick', - pcall_err(curbufmeths.del_var, 'changedtick')) - eq(ct, changedtick()) - - eq('\nE46: Cannot change read-only variable "b:["changedtick"]"', - redir_exec('let b:["changedtick"] += ' .. ctn)) - eq('\nE46: Cannot change read-only variable "b:["changedtick"]"', - redir_exec('let b:["changedtick"] -= ' .. ctn)) - eq('\nE46: Cannot change read-only variable "b:["changedtick"]"', - redir_exec('let b:["changedtick"] .= ' .. ctn)) - - eq(ct, changedtick()) - - funcs.setline(1, 'hello') - - eq(ct + 1, changedtick()) - end) - it('is listed in :let output', function() - eq('\nb:changedtick #2', - redir_exec(':let b:')) - end) - it('fails to unlock b:changedtick', function() - eq(0, exc_exec('let d = b:')) - eq(0, funcs.islocked('b:changedtick')) - eq(0, funcs.islocked('d.changedtick')) - eq('\nE940: Cannot lock or unlock variable b:changedtick', - redir_exec('unlockvar b:changedtick')) - eq('\nE46: Cannot change read-only variable "d.changedtick"', - redir_exec('unlockvar d.changedtick')) - eq(0, funcs.islocked('b:changedtick')) - eq(0, funcs.islocked('d.changedtick')) - eq('\nE940: Cannot lock or unlock variable b:changedtick', - redir_exec('lockvar b:changedtick')) - eq('\nE46: Cannot change read-only variable "d.changedtick"', - redir_exec('lockvar d.changedtick')) - eq(0, funcs.islocked('b:changedtick')) - eq(0, funcs.islocked('d.changedtick')) - end) - it('is being completed', function() - feed(':echo b:let cmdline=""') - eq('echo b:changedtick', meths.get_var('cmdline')) - end) - it('cannot be changed by filter() or map()', function() - eq(2, changedtick()) - eq('\nE795: Cannot delete variable filter() argument', - redir_exec('call filter(b:, 0)')) - eq('\nE742: Cannot change value of map() argument', - redir_exec('call map(b:, 0)')) - eq('\nE742: Cannot change value of map() argument', - redir_exec('call map(b:, "v:val")')) - eq(2, changedtick()) - end) - it('cannot be remove()d', function() - eq(2, changedtick()) - eq('\nE795: Cannot delete variable remove() argument', - redir_exec('call remove(b:, "changedtick")')) - eq(2, changedtick()) - end) - it('does not inherit VAR_FIXED when copying dictionary over', function() - eq(2, changedtick()) - eq('', redir_exec('let d1 = copy(b:)|let d1.changedtick = 42')) - eq('', redir_exec('let d2 = copy(b:)|unlet d2.changedtick')) - eq(2, changedtick()) - end) -end) diff --git a/test/functional/eval/container_functions_spec.lua b/test/functional/eval/container_functions_spec.lua deleted file mode 100644 index 04a3248c49..0000000000 --- a/test/functional/eval/container_functions_spec.lua +++ /dev/null @@ -1,24 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local eq = helpers.eq -local eval = helpers.eval -local meths = helpers.meths -local clear = helpers.clear - -before_each(clear) - -describe('extend()', function() - it('suceeds to extend list with itself', function() - meths.set_var('l', {1, {}}) - eq({1, {}, 1, {}}, eval('extend(l, l)')) - eq({1, {}, 1, {}}, meths.get_var('l')) - - meths.set_var('l', {1, {}}) - eq({1, {}, 1, {}}, eval('extend(l, l, 0)')) - eq({1, {}, 1, {}}, meths.get_var('l')) - - meths.set_var('l', {1, {}}) - eq({1, 1, {}, {}}, eval('extend(l, l, 1)')) - eq({1, 1, {}, {}}, meths.get_var('l')) - end) -end) diff --git a/test/functional/eval/ctx_functions_spec.lua b/test/functional/eval/ctx_functions_spec.lua deleted file mode 100644 index f23adbc556..0000000000 --- a/test/functional/eval/ctx_functions_spec.lua +++ /dev/null @@ -1,406 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local call = helpers.call -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed -local map = helpers.tbl_map -local nvim = helpers.nvim -local parse_context = helpers.parse_context -local redir_exec = helpers.redir_exec -local source = helpers.source -local trim = helpers.trim -local write_file = helpers.write_file -local pcall_err = helpers.pcall_err - -describe('context functions', function() - local fname1 = 'Xtest-functional-eval-ctx1' - local fname2 = 'Xtest-functional-eval-ctx2' - local outofbounds = - 'Vim:E475: Invalid value for argument index: out of bounds' - - before_each(function() - clear() - write_file(fname1, "1\n2\n3") - write_file(fname2, "a\nb\nc") - end) - - after_each(function() - os.remove(fname1) - os.remove(fname2) - end) - - describe('ctxpush/ctxpop', function() - it('saves and restores registers properly', function() - local regs = {'1', '2', '3', 'a'} - local vals = {'1', '2', '3', 'hjkl'} - feed('i123ddddddqahjklq') - eq(vals, map(function(r) return trim(call('getreg', r)) end, regs)) - call('ctxpush') - call('ctxpush', {'regs'}) - - map(function(r) call('setreg', r, {}) end, regs) - eq({'', '', '', ''}, - map(function(r) return trim(call('getreg', r)) end, regs)) - - call('ctxpop') - eq(vals, map(function(r) return trim(call('getreg', r)) end, regs)) - - map(function(r) call('setreg', r, {}) end, regs) - eq({'', '', '', ''}, - map(function(r) return trim(call('getreg', r)) end, regs)) - - call('ctxpop') - eq(vals, map(function(r) return trim(call('getreg', r)) end, regs)) - end) - - it('saves and restores jumplist properly', function() - command('edit '..fname1) - feed('G') - feed('gg') - command('edit '..fname2) - local jumplist = call('getjumplist') - call('ctxpush') - call('ctxpush', {'jumps'}) - - command('clearjumps') - eq({{}, 0}, call('getjumplist')) - - call('ctxpop') - eq(jumplist, call('getjumplist')) - - command('clearjumps') - eq({{}, 0}, call('getjumplist')) - - call('ctxpop') - eq(jumplist, call('getjumplist')) - end) - - it('saves and restores buffer list properly', function() - command('edit '..fname1) - command('edit '..fname2) - command('edit TEST') - local bufs = call('map', call('getbufinfo'), 'v:val.name') - call('ctxpush') - call('ctxpush', {'bufs'}) - - command('%bwipeout') - eq({''}, call('map', call('getbufinfo'), 'v:val.name')) - - call('ctxpop') - eq({'', unpack(bufs)}, call('map', call('getbufinfo'), 'v:val.name')) - - command('%bwipeout') - eq({''}, call('map', call('getbufinfo'), 'v:val.name')) - - call('ctxpop') - eq({'', unpack(bufs)}, call('map', call('getbufinfo'), 'v:val.name')) - end) - - it('saves and restores global variables properly', function() - nvim('set_var', 'one', 1) - nvim('set_var', 'Two', 2) - nvim('set_var', 'THREE', 3) - eq({1, 2 ,3}, eval('[g:one, g:Two, g:THREE]')) - call('ctxpush') - call('ctxpush', {'gvars'}) - - nvim('del_var', 'one') - nvim('del_var', 'Two') - nvim('del_var', 'THREE') - eq('Vim:E121: Undefined variable: g:one', pcall_err(eval, 'g:one')) - eq('Vim:E121: Undefined variable: g:Two', pcall_err(eval, 'g:Two')) - eq('Vim:E121: Undefined variable: g:THREE', pcall_err(eval, 'g:THREE')) - - call('ctxpop') - eq({1, 2 ,3}, eval('[g:one, g:Two, g:THREE]')) - - nvim('del_var', 'one') - nvim('del_var', 'Two') - nvim('del_var', 'THREE') - eq('Vim:E121: Undefined variable: g:one', pcall_err(eval, 'g:one')) - eq('Vim:E121: Undefined variable: g:Two', pcall_err(eval, 'g:Two')) - eq('Vim:E121: Undefined variable: g:THREE', pcall_err(eval, 'g:THREE')) - - call('ctxpop') - eq({1, 2 ,3}, eval('[g:one, g:Two, g:THREE]')) - end) - - it('saves and restores script functions properly', function() - source([[ - function s:greet(name) - echom 'Hello, '.a:name.'!' - endfunction - - function s:greet_all(name, ...) - echom 'Hello, '.a:name.'!' - for more in a:000 - echom 'Hello, '.more.'!' - endfor - endfunction - - function Greet(name) - call call('s:greet', [a:name]) - endfunction - - function GreetAll(name, ...) - call call('s:greet_all', extend([a:name], a:000)) - endfunction - - function SaveSFuncs() - call ctxpush(['sfuncs']) - endfunction - - function DeleteSFuncs() - delfunction s:greet - delfunction s:greet_all - endfunction - - function RestoreFuncs() - call ctxpop() - endfunction - ]]) - - eq('\nHello, World!', redir_exec([[call Greet('World')]])) - eq('\nHello, World!'.. - '\nHello, One!'.. - '\nHello, Two!'.. - '\nHello, Three!', - redir_exec([[call GreetAll('World', 'One', 'Two', 'Three')]])) - - call('SaveSFuncs') - call('DeleteSFuncs') - - eq('\nError detected while processing function Greet:'.. - '\nline 1:'.. - '\nE117: Unknown function: s:greet', - redir_exec([[call Greet('World')]])) - eq('\nError detected while processing function GreetAll:'.. - '\nline 1:'.. - '\nE117: Unknown function: s:greet_all', - redir_exec([[call GreetAll('World', 'One', 'Two', 'Three')]])) - - call('RestoreFuncs') - - eq('\nHello, World!', redir_exec([[call Greet('World')]])) - eq('\nHello, World!'.. - '\nHello, One!'.. - '\nHello, Two!'.. - '\nHello, Three!', - redir_exec([[call GreetAll('World', 'One', 'Two', 'Three')]])) - end) - - it('saves and restores functions properly', function() - source([[ - function Greet(name) - echom 'Hello, '.a:name.'!' - endfunction - - function GreetAll(name, ...) - echom 'Hello, '.a:name.'!' - for more in a:000 - echom 'Hello, '.more.'!' - endfor - endfunction - ]]) - - eq('\nHello, World!', redir_exec([[call Greet('World')]])) - eq('\nHello, World!'.. - '\nHello, One!'.. - '\nHello, Two!'.. - '\nHello, Three!', - redir_exec([[call GreetAll('World', 'One', 'Two', 'Three')]])) - - call('ctxpush', {'funcs'}) - command('delfunction Greet') - command('delfunction GreetAll') - - eq('Vim:E117: Unknown function: Greet', pcall_err(call, 'Greet', 'World')) - eq('Vim:E117: Unknown function: GreetAll', - pcall_err(call, 'GreetAll', 'World', 'One', 'Two', 'Three')) - - call('ctxpop') - - eq('\nHello, World!', redir_exec([[call Greet('World')]])) - eq('\nHello, World!'.. - '\nHello, One!'.. - '\nHello, Two!'.. - '\nHello, Three!', - redir_exec([[call GreetAll('World', 'One', 'Two', 'Three')]])) - end) - - it('errors out when context stack is empty', function() - local err = 'Vim:Context stack is empty' - eq(err, pcall_err(call, 'ctxpop')) - eq(err, pcall_err(call, 'ctxpop')) - call('ctxpush') - call('ctxpush') - call('ctxpop') - call('ctxpop') - eq(err, pcall_err(call, 'ctxpop')) - end) - end) - - describe('ctxsize()', function() - it('returns context stack size', function() - eq(0, call('ctxsize')) - call('ctxpush') - eq(1, call('ctxsize')) - call('ctxpush') - eq(2, call('ctxsize')) - call('ctxpush') - eq(3, call('ctxsize')) - call('ctxpop') - eq(2, call('ctxsize')) - call('ctxpop') - eq(1, call('ctxsize')) - call('ctxpop') - eq(0, call('ctxsize')) - end) - end) - - describe('ctxget()', function() - it('errors out when index is out of bounds', function() - eq(outofbounds, pcall_err(call, 'ctxget')) - call('ctxpush') - eq(outofbounds, pcall_err(call, 'ctxget', 1)) - call('ctxpop') - eq(outofbounds, pcall_err(call, 'ctxget', 0)) - end) - - it('returns context dictionary at index in context stack', function() - feed('i123ddddddqahjklq') - command('edit! '..fname1) - feed('G') - feed('gg') - command('edit '..fname2) - nvim('set_var', 'one', 1) - nvim('set_var', 'Two', 2) - nvim('set_var', 'THREE', 3) - - local with_regs = { - ['regs'] = { - {['rt'] = 1, ['rc'] = {'1'}, ['n'] = 49, ['ru'] = true}, - {['rt'] = 1, ['rc'] = {'2'}, ['n'] = 50}, - {['rt'] = 1, ['rc'] = {'3'}, ['n'] = 51}, - {['rc'] = {'hjkl'}, ['n'] = 97}, - } - } - - local with_jumps = { - ['jumps'] = eval(([[ - filter(map(getjumplist()[0], 'filter( - { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum }, - { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)') - ]]):gsub('\n', '')) - } - - local with_bufs = { - ['bufs'] = eval([[ - filter(map(getbufinfo(), '{ "f": v:val.name }'), '!empty(v:val.f)') - ]]) - } - - local with_gvars = { - ['gvars'] = {{'one', 1}, {'Two', 2}, {'THREE', 3}} - } - - local with_all = { - ['regs'] = with_regs['regs'], - ['jumps'] = with_jumps['jumps'], - ['bufs'] = with_bufs['bufs'], - ['gvars'] = with_gvars['gvars'], - } - - call('ctxpush') - eq(with_all, parse_context(call('ctxget'))) - eq(with_all, parse_context(call('ctxget', 0))) - - call('ctxpush', {'gvars'}) - eq(with_gvars, parse_context(call('ctxget'))) - eq(with_gvars, parse_context(call('ctxget', 0))) - eq(with_all, parse_context(call('ctxget', 1))) - - call('ctxpush', {'bufs'}) - eq(with_bufs, parse_context(call('ctxget'))) - eq(with_bufs, parse_context(call('ctxget', 0))) - eq(with_gvars, parse_context(call('ctxget', 1))) - eq(with_all, parse_context(call('ctxget', 2))) - - call('ctxpush', {'jumps'}) - eq(with_jumps, parse_context(call('ctxget'))) - eq(with_jumps, parse_context(call('ctxget', 0))) - eq(with_bufs, parse_context(call('ctxget', 1))) - eq(with_gvars, parse_context(call('ctxget', 2))) - eq(with_all, parse_context(call('ctxget', 3))) - - call('ctxpush', {'regs'}) - eq(with_regs, parse_context(call('ctxget'))) - eq(with_regs, parse_context(call('ctxget', 0))) - eq(with_jumps, parse_context(call('ctxget', 1))) - eq(with_bufs, parse_context(call('ctxget', 2))) - eq(with_gvars, parse_context(call('ctxget', 3))) - eq(with_all, parse_context(call('ctxget', 4))) - - call('ctxpop') - eq(with_jumps, parse_context(call('ctxget'))) - eq(with_jumps, parse_context(call('ctxget', 0))) - eq(with_bufs, parse_context(call('ctxget', 1))) - eq(with_gvars, parse_context(call('ctxget', 2))) - eq(with_all, parse_context(call('ctxget', 3))) - - call('ctxpop') - eq(with_bufs, parse_context(call('ctxget'))) - eq(with_bufs, parse_context(call('ctxget', 0))) - eq(with_gvars, parse_context(call('ctxget', 1))) - eq(with_all, parse_context(call('ctxget', 2))) - - call('ctxpop') - eq(with_gvars, parse_context(call('ctxget'))) - eq(with_gvars, parse_context(call('ctxget', 0))) - eq(with_all, parse_context(call('ctxget', 1))) - - call('ctxpop') - eq(with_all, parse_context(call('ctxget'))) - eq(with_all, parse_context(call('ctxget', 0))) - end) - end) - - describe('ctxset()', function() - it('errors out when index is out of bounds', function() - eq(outofbounds, pcall_err(call, 'ctxset', {dummy = 1})) - call('ctxpush') - eq(outofbounds, pcall_err(call, 'ctxset', {dummy = 1}, 1)) - call('ctxpop') - eq(outofbounds, pcall_err(call, 'ctxset', {dummy = 1}, 0)) - end) - - it('sets context dictionary at index in context stack', function() - nvim('set_var', 'one', 1) - nvim('set_var', 'Two', 2) - nvim('set_var', 'THREE', 3) - call('ctxpush') - local ctx1 = call('ctxget') - nvim('set_var', 'one', 'a') - nvim('set_var', 'Two', 'b') - nvim('set_var', 'THREE', 'c') - call('ctxpush') - call('ctxpush') - local ctx2 = call('ctxget') - - eq({'a', 'b' ,'c'}, eval('[g:one, g:Two, g:THREE]')) - call('ctxset', ctx1) - call('ctxset', ctx2, 2) - call('ctxpop') - eq({1, 2 ,3}, eval('[g:one, g:Two, g:THREE]')) - call('ctxpop') - eq({'a', 'b' ,'c'}, eval('[g:one, g:Two, g:THREE]')) - nvim('set_var', 'one', 1.5) - eq({1.5, 'b' ,'c'}, eval('[g:one, g:Two, g:THREE]')) - call('ctxpop') - eq({'a', 'b' ,'c'}, eval('[g:one, g:Two, g:THREE]')) - end) - end) -end) diff --git a/test/functional/eval/environ_spec.lua b/test/functional/eval/environ_spec.lua deleted file mode 100644 index 9e19568249..0000000000 --- a/test/functional/eval/environ_spec.lua +++ /dev/null @@ -1,80 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq -local environ = helpers.funcs.environ -local exists = helpers.funcs.exists -local system = helpers.funcs.system -local nvim_prog = helpers.nvim_prog -local command = helpers.command -local eval = helpers.eval -local setenv = helpers.funcs.setenv - -describe('environment variables', function() - it('environ() handles empty env variable', function() - clear({env={EMPTY_VAR=""}}) - eq("", environ()['EMPTY_VAR']) - eq(nil, environ()['DOES_NOT_EXIST']) - end) - - it('exists() handles empty env variable', function() - clear({env={EMPTY_VAR=""}}) - eq(1, exists('$EMPTY_VAR')) - eq(0, exists('$DOES_NOT_EXIST')) - end) -end) - -describe('empty $HOME', function() - local original_home = os.getenv('HOME') - - -- recover $HOME after each test - after_each(function() - if original_home ~= nil then - setenv('HOME', original_home) - end - os.remove('test_empty_home') - os.remove('./~') - end) - - local function tilde_in_cwd() - -- get files in cwd - command("let test_empty_home_cwd_files = split(globpath('.', '*'), '\n')") - -- get the index of the file named '~' - command('let test_empty_home_tilde_index = index(test_empty_home_cwd_files, "./~")') - return eval('test_empty_home_tilde_index') ~= -1 - end - - local function write_and_test_tilde() - system({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', - '-c', 'write test_empty_home', '+q'}) - eq(false, tilde_in_cwd()) - end - - it("'~' folder not created in cwd if $HOME and related env not defined", function() - command("unlet $HOME") - write_and_test_tilde() - - command("let $HOMEDRIVE='C:'") - command("let $USERPROFILE='C:\\'") - write_and_test_tilde() - - command("unlet $HOMEDRIVE") - write_and_test_tilde() - - command("unlet $USERPROFILE") - write_and_test_tilde() - - command("let $HOME='%USERPROFILE%'") - command("let $USERPROFILE='C:\\'") - write_and_test_tilde() - end) - - it("'~' folder not created in cwd if writing a file with invalid $HOME", function() - setenv('HOME', '/path/does/not/exist') - write_and_test_tilde() - end) - - it("'~' folder not created in cwd if writing a file with $HOME=''", function() - command("let $HOME=''") - write_and_test_tilde() - end) -end) diff --git a/test/functional/eval/executable_spec.lua b/test/functional/eval/executable_spec.lua deleted file mode 100644 index 28aefb72e5..0000000000 --- a/test/functional/eval/executable_spec.lua +++ /dev/null @@ -1,218 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq, clear, call, iswin, write_file, command = - helpers.eq, helpers.clear, helpers.call, helpers.iswin, helpers.write_file, - helpers.command -local exc_exec = helpers.exc_exec -local eval = helpers.eval - -describe('executable()', function() - before_each(clear) - - it('returns 1 for commands in $PATH', function() - local exe = iswin() and 'ping' or 'ls' - eq(1, call('executable', exe)) - command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') - eq(1, call('executable', 'null')) - eq(1, call('executable', 'true')) - eq(1, call('executable', 'false')) - end) - - it('fails for invalid values', function() - for _, input in ipairs({'""', 'v:null', 'v:true', 'v:false', '{}', '[]'}) do - eq('Vim(call):E928: String required', exc_exec('call executable('..input..')')) - end - command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') - for _, input in ipairs({'v:null', 'v:true', 'v:false'}) do - eq('Vim(call):E928: String required', exc_exec('call executable('..input..')')) - end - end) - - it('returns 0 for non-existent files', function() - eq(0, call('executable', 'no_such_file_exists_209ufq23f')) - end) - - it('sibling to nvim binary', function() - -- Some executable in build/bin/, *not* in $PATH nor CWD. - local sibling_exe = 'printargs-test' - -- Windows: siblings are in Nvim's "pseudo-$PATH". - local expected = iswin() and 1 or 0 - if iswin() then - eq('arg1=lemon;arg2=sky;arg3=tree;', - call('system', sibling_exe..' lemon sky tree')) - end - eq(expected, call('executable', sibling_exe)) - end) - - describe('exec-bit', function() - setup(function() - clear() - write_file('Xtest_not_executable', 'non-executable file') - write_file('Xtest_executable', 'executable file (exec-bit set)') - if not iswin() then -- N/A for Windows. - call('system', {'chmod', '-x', 'Xtest_not_executable'}) - call('system', {'chmod', '+x', 'Xtest_executable'}) - end - end) - - teardown(function() - os.remove('Xtest_not_executable') - os.remove('Xtest_executable') - end) - - it('not set', function() - eq(0, call('executable', 'Xtest_not_executable')) - eq(0, call('executable', './Xtest_not_executable')) - end) - - it('set, unqualified and not in $PATH', function() - eq(0, call('executable', 'Xtest_executable')) - end) - - it('set, qualified as a path', function() - local expected = iswin() and 0 or 1 - eq(expected, call('executable', './Xtest_executable')) - end) - end) -end) - -describe('executable() (Windows)', function() - if not iswin() then return end -- N/A for Unix. - - local exts = {'bat', 'exe', 'com', 'cmd'} - setup(function() - for _, ext in ipairs(exts) do - write_file('test_executable_'..ext..'.'..ext, '') - end - write_file('test_executable_zzz.zzz', '') - end) - - teardown(function() - for _, ext in ipairs(exts) do - os.remove('test_executable_'..ext..'.'..ext) - end - os.remove('test_executable_zzz.zzz') - end) - - it('tries default extensions on a filename if $PATHEXT is empty', function() - -- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd". - clear({env={PATHEXT=''}}) - for _,ext in ipairs(exts) do - eq(1, call('executable', 'test_executable_'..ext)) - end - eq(0, call('executable', 'test_executable_zzz')) - end) - - it('tries default extensions on a filepath if $PATHEXT is empty', function() - -- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd". - clear({env={PATHEXT=''}}) - for _,ext in ipairs(exts) do - eq(1, call('executable', '.\\test_executable_'..ext)) - end - eq(0, call('executable', '.\\test_executable_zzz')) - end) - - it('system([…]), jobstart([…]) use $PATHEXT #9569', function() - -- Invoking `cmdscript` should find/execute `cmdscript.cmd`. - eq('much success\n', call('system', {'test/functional/fixtures/cmdscript'})) - assert(0 < call('jobstart', {'test/functional/fixtures/cmdscript'})) - end) - - it('full path with extension', function() - -- Some executable we can expect in the test env. - local exe = 'printargs-test' - local exedir = eval("fnamemodify(v:progpath, ':h')") - local exepath = exedir..'/'..exe..'.exe' - eq(1, call('executable', exepath)) - eq('arg1=lemon;arg2=sky;arg3=tree;', - call('system', exepath..' lemon sky tree')) - end) - - it('full path without extension', function() - -- Some executable we can expect in the test env. - local exe = 'printargs-test' - local exedir = eval("fnamemodify(v:progpath, ':h')") - local exepath = exedir..'/'..exe - eq('arg1=lemon;arg2=sky;arg3=tree;', - call('system', exepath..' lemon sky tree')) - eq(1, call('executable', exepath)) - end) - - it('respects $PATHEXT when trying extensions on a filename', function() - clear({env={PATHEXT='.zzz'}}) - for _,ext in ipairs(exts) do - eq(0, call('executable', 'test_executable_'..ext)) - end - eq(1, call('executable', 'test_executable_zzz')) - end) - - it('respects $PATHEXT when trying extensions on a filepath', function() - clear({env={PATHEXT='.zzz'}}) - for _,ext in ipairs(exts) do - eq(0, call('executable', '.\\test_executable_'..ext)) - end - eq(1, call('executable', '.\\test_executable_zzz')) - end) - - it("with weird $PATHEXT", function() - clear({env={PATHEXT=';'}}) - eq(0, call('executable', '.\\test_executable_zzz')) - clear({env={PATHEXT=';;;.zzz;;'}}) - eq(1, call('executable', '.\\test_executable_zzz')) - end) - - it("unqualified filename, Unix-style 'shell'", function() - clear({env={PATHEXT=''}}) - command('set shell=sh') - for _,ext in ipairs(exts) do - eq(1, call('executable', 'test_executable_'..ext..'.'..ext)) - end - eq(1, call('executable', 'test_executable_zzz.zzz')) - end) - - it("relative path, Unix-style 'shell' (backslashes)", function() - clear({env={PATHEXT=''}}) - command('set shell=bash.exe') - for _,ext in ipairs(exts) do - eq(1, call('executable', '.\\test_executable_'..ext..'.'..ext)) - eq(1, call('executable', './test_executable_'..ext..'.'..ext)) - end - eq(1, call('executable', '.\\test_executable_zzz.zzz')) - eq(1, call('executable', './test_executable_zzz.zzz')) - end) - - it('unqualified filename, $PATHEXT contains dot', function() - clear({env={PATHEXT='.;.zzz'}}) - for _,ext in ipairs(exts) do - eq(1, call('executable', 'test_executable_'..ext..'.'..ext)) - end - eq(1, call('executable', 'test_executable_zzz.zzz')) - clear({env={PATHEXT='.zzz;.'}}) - for _,ext in ipairs(exts) do - eq(1, call('executable', 'test_executable_'..ext..'.'..ext)) - end - eq(1, call('executable', 'test_executable_zzz.zzz')) - end) - - it('relative path, $PATHEXT contains dot (backslashes)', function() - clear({env={PATHEXT='.;.zzz'}}) - for _,ext in ipairs(exts) do - eq(1, call('executable', '.\\test_executable_'..ext..'.'..ext)) - eq(1, call('executable', './test_executable_'..ext..'.'..ext)) - end - eq(1, call('executable', '.\\test_executable_zzz.zzz')) - eq(1, call('executable', './test_executable_zzz.zzz')) - end) - - it('ignores case of extension', function() - clear({env={PATHEXT='.ZZZ'}}) - eq(1, call('executable', 'test_executable_zzz.zzz')) - end) - - it('relative path does not search $PATH', function() - clear({env={PATHEXT=''}}) - eq(0, call('executable', './System32/notepad.exe')) - eq(0, call('executable', '.\\System32\\notepad.exe')) - eq(0, call('executable', '../notepad.exe')) - eq(0, call('executable', '..\\notepad.exe')) - end) -end) diff --git a/test/functional/eval/execute_spec.lua b/test/functional/eval/execute_spec.lua deleted file mode 100644 index fccf52935b..0000000000 --- a/test/functional/eval/execute_spec.lua +++ /dev/null @@ -1,337 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq -local eval = helpers.eval -local clear = helpers.clear -local source = helpers.source -local redir_exec = helpers.redir_exec -local exc_exec = helpers.exc_exec -local funcs = helpers.funcs -local Screen = require('test.functional.ui.screen') -local command = helpers.command -local feed = helpers.feed -local iswin = helpers.iswin - -describe('execute()', function() - before_each(clear) - - it('captures the same result as :redir', function() - eq(redir_exec('messages'), funcs.execute('messages')) - end) - - it('captures the concatenated outputs of a List of commands', function() - eq("foobar", funcs.execute({'echon "foo"', 'echon "bar"'})) - eq("\nfoo\nbar", funcs.execute({'echo "foo"', 'echo "bar"'})) - end) - - it('supports nested execute("execute(...)")', function() - eq('42', funcs.execute([[echon execute("echon execute('echon 42')")]])) - end) - - it('supports nested :redir to a variable', function() - source([[ - function! g:Foo() - let a = '' - redir => a - silent echon "foo" - redir END - return a - endfunction - function! g:Bar() - let a = '' - redir => a - silent echon "bar1" - call g:Foo() - silent echon "bar2" - redir END - silent echon "bar3" - return a - endfunction - ]]) - eq('top1bar1foobar2bar3', funcs.execute('echon "top1"|call g:Bar()')) - end) - - it('supports nested :redir to a register', function() - source([[ - let @a = '' - function! g:Foo() - redir @a>> - silent echon "foo" - redir END - return @a - endfunction - function! g:Bar() - redir @a>> - silent echon "bar1" - call g:Foo() - silent echon "bar2" - redir END - silent echon "bar3" - return @a - endfunction - ]]) - eq('top1bar1foobar2bar3', funcs.execute('echon "top1"|call g:Bar()')) - -- :redir itself doesn't nest, so the redirection ends in g:Foo - eq('bar1foo', eval('@a')) - end) - - it('captures a transformed string', function() - eq('^A', funcs.execute('echon "\\"')) - end) - - it('returns empty string if the argument list is empty', function() - eq('', funcs.execute({})) - eq(0, exc_exec('let g:ret = execute(v:_null_list)')) - eq('', eval('g:ret')) - end) - - it('captures errors', function() - local ret - ret = exc_exec('call execute(0.0)') - eq('Vim(call):E806: using Float as a String', ret) - ret = exc_exec('call execute(v:_null_dict)') - eq('Vim(call):E731: using Dictionary as a String', ret) - ret = exc_exec('call execute(function("tr"))') - eq('Vim(call):E729: using Funcref as a String', ret) - ret = exc_exec('call execute(["echo 42", 0.0, "echo 44"])') - eq('Vim:E806: using Float as a String', ret) - ret = exc_exec('call execute(["echo 42", v:_null_dict, "echo 44"])') - eq('Vim:E731: using Dictionary as a String', ret) - ret = exc_exec('call execute(["echo 42", function("tr"), "echo 44"])') - eq('Vim:E729: using Funcref as a String', ret) - end) - - it('captures output with highlights', function() - eq('\nErrorMsg xxx ctermfg=15 ctermbg=1 guifg=White guibg=Red', - eval('execute("hi ErrorMsg")')) - end) - - it('does not corrupt the command display #5422', function() - local screen = Screen.new(70, 7) - screen:attach() - feed(':echo execute("hi ErrorMsg")') - screen:expect([[ - | - {1:~ }| - {1:~ }| - {2: }| - | - ErrorMsg xxx ctermfg=15 ctermbg=1 guifg=White guibg=Red | - {3:Press ENTER or type command to continue}^ | - ]], { - [1] = {bold = true, foreground = Screen.colors.Blue1}, - [2] = {bold = true, reverse = true}, - [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, - }) - feed('') - end) - - it('places cursor correctly #6035', function() - local screen = Screen.new(40, 6) - screen:attach() - source([=[ - " test 1: non-silenced output goes as usual - function! Test1() - echo 1234 - let x = execute('echon "abcdef"', '') - echon 'ABCD' - endfunction - - " test 2: silenced output does not affect ui - function! Test2() - echo 1234 - let x = execute('echon "abcdef"', 'silent') - echon 'ABCD' - endfunction - - " test 3: silenced! error does not affect ui - function! Test3() - echo 1234 - let x = execute('echoerr "abcdef"', 'silent!') - echon 'ABCD' - endfunction - - " test 4: silenced echoerr goes as usual - " bug here - function! Test4() - echo 1234 - let x = execute('echoerr "abcdef"', 'silent') - echon 'ABCD' - endfunction - - " test 5: silenced! echoerr does not affect ui - function! Test5() - echo 1234 - let x = execute('echoerr "abcdef"', 'silent!') - echon 'ABCD' - endfunction - - " test 6: silenced error goes as usual - function! Test6() - echo 1234 - let x = execute('echo undefined', 'silent') - echon 'ABCD' - endfunction - - " test 7: existing error does not mess the result - function! Test7() - " display from Test6() is still visible - " why does the "abcdef" goes into a newline - let x = execute('echon "abcdef"', '') - echon 'ABCD' - endfunction - ]=]) - - feed([[:call Test1()]]) - screen:expect([[ - ^ | - ~ | - ~ | - ~ | - ~ | - ABCD | - ]]) - - feed([[:call Test2()]]) - screen:expect([[ - ^ | - ~ | - ~ | - ~ | - ~ | - 1234ABCD | - ]]) - - feed([[:call Test3()]]) - screen:expect([[ - ^ | - ~ | - ~ | - ~ | - ~ | - 1234ABCD | - ]]) - - feed([[:call Test4()]]) - -- unexpected: need to fix - -- echoerr does not set did_emsg - -- "ef" was overwritten since msg_col was recovered wrongly - screen:expect([[ - 1234 | - Error detected while processing function| - Test4: | - line 2: | - abcdABCD | - Press ENTER or type command to continue^ | - ]]) - - feed([[]]) -- to clear screen - feed([[:call Test5()]]) - screen:expect([[ - ^ | - ~ | - ~ | - ~ | - ~ | - 1234ABCD | - ]]) - - feed([[:call Test6()]]) - screen:expect([[ - | - Error detected while processing function| - Test6: | - line 2: | - E121ABCD | - Press ENTER or type command to continue^ | - ]]) - - feed([[:call Test7()]]) - screen:expect([[ - Error detected while processing function| - Test6: | - line 2: | - E121ABCD | - ABCD | - Press ENTER or type command to continue^ | - ]]) - end) - - -- This deviates from vim behavior, but is consistent - -- with how nvim currently displays the output. - it('captures shell-command output', function() - local win_lf = iswin() and '\13' or '' - eq('\n:!echo foo\r\n\nfoo'..win_lf..'\n', funcs.execute('!echo foo')) - end) - - describe('{silent} argument', function() - it('captures & displays output for ""', function() - local screen = Screen.new(40, 5) - screen:attach() - command('let g:mes = execute("echon 42", "")') - screen:expect([[ - ^ | - ~ | - ~ | - ~ | - 42 | - ]]) - eq('42', eval('g:mes')) - end) - - it('captures but does not display output for "silent"', function() - local screen = Screen.new(40, 5) - screen:attach() - command('let g:mes = execute("echon 42")') - screen:expect([[ - ^ | - ~ | - ~ | - ~ | - | - ]]) - eq('42', eval('g:mes')) - - command('let g:mes = execute("echon 13", "silent")') - screen:expect{grid=[[ - ^ | - ~ | - ~ | - ~ | - | - ]], unchanged=true} - eq('13', eval('g:mes')) - end) - - it('suppresses errors for "silent!"', function() - eq(0, exc_exec('let g:mes = execute(0.0, "silent!")')) - eq('', eval('g:mes')) - - eq(0, exc_exec('let g:mes = execute("echon add(1, 1)", "silent!")')) - eq('1', eval('g:mes')) - - eq(0, exc_exec('let g:mes = execute(["echon 42", "echon add(1, 1)"], "silent!")')) - eq('421', eval('g:mes')) - end) - - it('propagates errors for "" and "silent"', function() - local ret - ret = exc_exec('call execute(0.0, "")') - eq('Vim(call):E806: using Float as a String', ret) - - ret = exc_exec('call execute(v:_null_dict, "silent")') - eq('Vim(call):E731: using Dictionary as a String', ret) - - ret = exc_exec('call execute("echo add(1, 1)", "")') - eq('Vim(echo):E897: List or Blob required', ret) - - ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "")') - eq('Vim(echo):E897: List or Blob required', ret) - - ret = exc_exec('call execute("echo add(1, 1)", "silent")') - eq('Vim(echo):E897: List or Blob required', ret) - - ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "silent")') - eq('Vim(echo):E897: List or Blob required', ret) - end) - end) -end) diff --git a/test/functional/eval/exepath_spec.lua b/test/functional/eval/exepath_spec.lua deleted file mode 100644 index 08d2c59af8..0000000000 --- a/test/functional/eval/exepath_spec.lua +++ /dev/null @@ -1,40 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq, clear, call, iswin = - helpers.eq, helpers.clear, helpers.call, helpers.iswin -local command = helpers.command -local exc_exec = helpers.exc_exec -local matches = helpers.matches - -describe('exepath()', function() - before_each(clear) - - it('returns 1 for commands in $PATH', function() - local exe = iswin() and 'ping' or 'ls' - local ext_pat = iswin() and '%.EXE$' or '$' - matches(exe .. ext_pat, call('exepath', exe)) - command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') - ext_pat = iswin() and '%.CMD$' or '$' - matches('null' .. ext_pat, call('exepath', 'null')) - matches('true' .. ext_pat, call('exepath', 'true')) - matches('false' .. ext_pat, call('exepath', 'false')) - end) - - it('fails for invalid values', function() - for _, input in ipairs({'""', 'v:null', 'v:true', 'v:false', '{}', '[]'}) do - eq('Vim(call):E928: String required', exc_exec('call exepath('..input..')')) - end - command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') - for _, input in ipairs({'v:null', 'v:true', 'v:false'}) do - eq('Vim(call):E928: String required', exc_exec('call exepath('..input..')')) - end - end) - - if iswin() then - it('append extension if omitted', function() - local filename = 'cmd' - local pathext = '.exe' - clear({env={PATHEXT=pathext}}) - eq(call('exepath', filename..pathext), call('exepath', filename)) - end) - end -end) diff --git a/test/functional/eval/fnamemodify_spec.lua b/test/functional/eval/fnamemodify_spec.lua deleted file mode 100644 index d54a6db417..0000000000 --- a/test/functional/eval/fnamemodify_spec.lua +++ /dev/null @@ -1,156 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq -local iswin = helpers.iswin -local fnamemodify = helpers.funcs.fnamemodify -local getcwd = helpers.funcs.getcwd -local command = helpers.command -local write_file = helpers.write_file -local alter_slashes = helpers.alter_slashes - -local function eq_slashconvert(expected, got) - eq(alter_slashes(expected), alter_slashes(got)) -end - -describe('fnamemodify()', function() - setup(function() - write_file('Xtest-fnamemodify.txt', [[foobar]]) - end) - - before_each(clear) - - teardown(function() - os.remove('Xtest-fnamemodify.txt') - end) - - it('handles the root path', function() - local root = helpers.pathroot() - eq(root, fnamemodify([[/]], ':p:h')) - eq(root, fnamemodify([[/]], ':p')) - if iswin() then - eq(root, fnamemodify([[\]], ':p:h')) - eq(root, fnamemodify([[\]], ':p')) - command('set shellslash') - root = string.sub(root, 1, -2)..'/' - eq(root, fnamemodify([[\]], ':p:h')) - eq(root, fnamemodify([[\]], ':p')) - eq(root, fnamemodify([[/]], ':p:h')) - eq(root, fnamemodify([[/]], ':p')) - end - end) - - it(':8 works', function() - eq('Xtest-fnamemodify.txt', fnamemodify([[Xtest-fnamemodify.txt]], ':8')) - end) - - it('handles examples from ":help filename-modifiers"', function() - local filename = "src/version.c" - local cwd = getcwd() - - eq_slashconvert(cwd .. '/src/version.c', fnamemodify(filename, ':p')) - - eq_slashconvert('src/version.c', fnamemodify(filename, ':p:.')) - eq_slashconvert(cwd .. '/src', fnamemodify(filename, ':p:h')) - eq_slashconvert(cwd .. '', fnamemodify(filename, ':p:h:h')) - eq('version.c', fnamemodify(filename, ':p:t')) - eq_slashconvert(cwd .. '/src/version', fnamemodify(filename, ':p:r')) - - eq_slashconvert(cwd .. '/src/main.c', fnamemodify(filename, ':s?version?main?:p')) - - local converted_cwd = cwd:gsub('/', '\\') - eq(converted_cwd .. '\\src\\version.c', fnamemodify(filename, ':p:gs?/?\\\\?')) - - eq('src', fnamemodify(filename, ':h')) - eq('version.c', fnamemodify(filename, ':t')) - eq_slashconvert('src/version', fnamemodify(filename, ':r')) - eq('version', fnamemodify(filename, ':t:r')) - eq('c', fnamemodify(filename, ':e')) - - eq_slashconvert('src/main.c', fnamemodify(filename, ':s?version?main?')) - end) - - it('handles advanced examples from ":help filename-modifiers"', function() - local filename = "src/version.c.gz" - - eq('gz', fnamemodify(filename, ':e')) - eq('c.gz', fnamemodify(filename, ':e:e')) - eq('c.gz', fnamemodify(filename, ':e:e:e')) - - eq('c', fnamemodify(filename, ':e:e:r')) - - eq_slashconvert('src/version.c', fnamemodify(filename, ':r')) - eq('c', fnamemodify(filename, ':r:e')) - - eq_slashconvert('src/version', fnamemodify(filename, ':r:r')) - eq_slashconvert('src/version', fnamemodify(filename, ':r:r:r')) - end) - - it('handles :h', function() - eq('.', fnamemodify('hello.txt', ':h')) - - eq_slashconvert('path/to', fnamemodify('path/to/hello.txt', ':h')) - end) - - it('handles :t', function() - eq('hello.txt', fnamemodify('hello.txt', ':t')) - eq_slashconvert('hello.txt', fnamemodify('path/to/hello.txt', ':t')) - end) - - it('handles :r', function() - eq('hello', fnamemodify('hello.txt', ':r')) - eq_slashconvert('path/to/hello', fnamemodify('path/to/hello.txt', ':r')) - end) - - it('handles :e', function() - eq('txt', fnamemodify('hello.txt', ':e')) - eq_slashconvert('txt', fnamemodify('path/to/hello.txt', ':e')) - end) - - it('handles regex replacements', function() - eq('content-there-here.txt', fnamemodify('content-here-here.txt', ':s/here/there/')) - eq('content-there-there.txt', fnamemodify('content-here-here.txt', ':gs/here/there/')) - end) - - it('handles shell escape', function() - local expected - - if iswin() then - -- we expand with double-quotes on Windows - expected = [["hello there! quote ' newline]] .. '\n' .. [["]] - else - expected = [['hello there! quote '\'' newline]] .. '\n' .. [[']] - end - - eq(expected, fnamemodify("hello there! quote ' newline\n", ':S')) - end) - - it('can combine :e and :r', function() - -- simple, single extension filename - eq('c', fnamemodify('a.c', ':e')) - eq('c', fnamemodify('a.c', ':e:e')) - eq('c', fnamemodify('a.c', ':e:e:r')) - eq('c', fnamemodify('a.c', ':e:e:r:r')) - - -- multi extension filename - eq('rb', fnamemodify('a.spec.rb', ':e:r')) - eq('rb', fnamemodify('a.spec.rb', ':e:r:r')) - - eq('spec', fnamemodify('a.spec.rb', ':e:e:r')) - eq('spec', fnamemodify('a.spec.rb', ':e:e:r:r')) - - eq('spec', fnamemodify('a.b.spec.rb', ':e:e:r')) - eq('b.spec', fnamemodify('a.b.spec.rb', ':e:e:e:r')) - eq('b', fnamemodify('a.b.spec.rb', ':e:e:e:r:r')) - - eq('spec', fnamemodify('a.b.spec.rb', ':r:e')) - eq('b', fnamemodify('a.b.spec.rb', ':r:r:e')) - - -- extraneous :e expansions - eq('c', fnamemodify('a.b.c.d.e', ':r:r:e')) - eq('b.c', fnamemodify('a.b.c.d.e', ':r:r:e:e')) - - -- :e never includes the whole filename, so "a.b":e:e:e --> "b" - eq('b.c', fnamemodify('a.b.c.d.e', ':r:r:e:e:e')) - eq('b.c', fnamemodify('a.b.c.d.e', ':r:r:e:e:e:e')) - end) -end) diff --git a/test/functional/eval/function_spec.lua b/test/functional/eval/function_spec.lua deleted file mode 100644 index ce8850fcc2..0000000000 --- a/test/functional/eval/function_spec.lua +++ /dev/null @@ -1,37 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local eq = helpers.eq -local matches = helpers.matches -local exc_exec = helpers.exc_exec -local iswin = helpers.iswin -local eval = helpers.eval - -describe('Up to MAX_FUNC_ARGS arguments are handled by', function() - local max_func_args = 20 -- from eval.h - local range = helpers.funcs.range - - before_each(clear) - - it('printf()', function() - local printf = helpers.funcs.printf - local rep = helpers.funcs['repeat'] - local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,' - eq(expected, printf(rep('%d,', max_func_args-1), unpack(range(2, max_func_args)))) - local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') - eq('Vim(call):E740: Too many arguments for function printf', ret) - end) - - it('rpcnotify()', function() - local rpcnotify = helpers.funcs.rpcnotify - local ret = rpcnotify(0, 'foo', unpack(range(3, max_func_args))) - eq(1, ret) - ret = exc_exec('call rpcnotify(0, "foo", 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') - eq('Vim(call):E740: Too many arguments for function rpcnotify', ret) - end) -end) - -it('windowsversion()', function() - clear() - matches(iswin() and '^%d+%.%d+$' or '^$', eval('windowsversion()')) -end) diff --git a/test/functional/eval/getline_spec.lua b/test/functional/eval/getline_spec.lua deleted file mode 100644 index 3c56bde094..0000000000 --- a/test/functional/eval/getline_spec.lua +++ /dev/null @@ -1,39 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local call = helpers.call -local clear = helpers.clear -local eq = helpers.eq -local expect = helpers.expect - -describe('getline()', function() - before_each(function() - clear() - call('setline', 1, {'a', 'b', 'c'}) - expect([[ - a - b - c]]) - end) - - it('returns empty string for invalid line', function() - eq('', call('getline', -1)) - eq('', call('getline', 0)) - eq('', call('getline', 4)) - end) - - it('returns empty list for invalid range', function() - eq({}, call('getline', 2, 1)) - eq({}, call('getline', -1, 1)) - eq({}, call('getline', 4, 4)) - end) - - it('returns value of valid line', function() - eq('b', call('getline', 2)) - eq('a', call('getline', '.')) - end) - - it('returns value of valid range', function() - eq({'a', 'b'}, call('getline', 1, 2)) - eq({'a', 'b', 'c'}, call('getline', 1, 4)) - end) -end) diff --git a/test/functional/eval/glob_spec.lua b/test/functional/eval/glob_spec.lua deleted file mode 100644 index b8807ecfcc..0000000000 --- a/test/functional/eval/glob_spec.lua +++ /dev/null @@ -1,28 +0,0 @@ -local lfs = require('lfs') -local helpers = require('test.functional.helpers')(after_each) -local clear, command, eval, eq = helpers.clear, helpers.command, helpers.eval, helpers.eq - -before_each(function() - clear() - lfs.mkdir('test-glob') - - -- Long path might cause "Press ENTER" prompt; use :silent to avoid it. - command('silent cd test-glob') -end) - -after_each(function() - lfs.rmdir('test-glob') -end) - -describe('glob()', function() - it("glob('.*') returns . and .. ", function() - eq({'.', '..'}, eval("glob('.*', 0, 1)")) - -- Do it again to verify scandir_next_with_dots() internal state. - eq({'.', '..'}, eval("glob('.*', 0, 1)")) - end) - it("glob('*') returns an empty list ", function() - eq({}, eval("glob('*', 0, 1)")) - -- Do it again to verify scandir_next_with_dots() internal state. - eq({}, eval("glob('*', 0, 1)")) - end) -end) diff --git a/test/functional/eval/has_spec.lua b/test/functional/eval/has_spec.lua deleted file mode 100644 index a3af2d1a20..0000000000 --- a/test/functional/eval/has_spec.lua +++ /dev/null @@ -1,66 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq -local clear = helpers.clear -local funcs = helpers.funcs -local iswin = helpers.iswin - -describe('has()', function() - before_each(clear) - - it('"nvim-x.y.z"', function() - eq(0, funcs.has("nvim-")) - eq(0, funcs.has("nvim- ")) - eq(0, funcs.has("nvim- \t ")) - eq(0, funcs.has("nvim-0. 1. 1")) - eq(0, funcs.has("nvim-0. 1.1")) - eq(0, funcs.has("nvim-0.1. 1")) - eq(0, funcs.has("nvim-a")) - eq(0, funcs.has("nvim-a.b.c")) - eq(0, funcs.has("nvim-0.b.c")) - eq(0, funcs.has("nvim-0.0.c")) - eq(0, funcs.has("nvim-0.b.0")) - eq(0, funcs.has("nvim-a.b.0")) - eq(0, funcs.has("nvim-.0.0.0")) - eq(0, funcs.has("nvim-.0")) - eq(0, funcs.has("nvim-0.")) - eq(0, funcs.has("nvim-0..")) - eq(0, funcs.has("nvim-.")) - eq(0, funcs.has("nvim-..")) - eq(0, funcs.has("nvim-...")) - eq(0, funcs.has("nvim-42")) - eq(0, funcs.has("nvim-9999")) - eq(0, funcs.has("nvim-99.001.05")) - - eq(1, funcs.has("nvim")) - eq(1, funcs.has("nvim-0")) - eq(1, funcs.has("nvim-0.1")) - eq(1, funcs.has("nvim-0.0.0")) - eq(1, funcs.has("nvim-0.1.1.")) - eq(1, funcs.has("nvim-0.1.1.abc")) - eq(1, funcs.has("nvim-0.1.1..")) - eq(1, funcs.has("nvim-0.1.1.. ..")) - eq(1, funcs.has("nvim-0.1.1.... ")) - eq(1, funcs.has("nvim-0.0.0")) - eq(1, funcs.has("nvim-0.0.1")) - eq(1, funcs.has("nvim-0.1.0")) - eq(1, funcs.has("nvim-0.1.1")) - eq(1, funcs.has("nvim-0.1.5")) - eq(1, funcs.has("nvim-0000.001.05")) - eq(1, funcs.has("nvim-0.01.005")) - eq(1, funcs.has("nvim-00.001.05")) - end) - - it('"unnamedplus"', function() - if (not iswin()) and funcs.has("clipboard") == 1 then - eq(1, funcs.has("unnamedplus")) - else - eq(0, funcs.has("unnamedplus")) - end - end) - - it('"wsl"', function() - if 1 == funcs.has('win32') or 1 == funcs.has('mac') then - eq(0, funcs.has('wsl')) - end - end) -end) diff --git a/test/functional/eval/hostname_spec.lua b/test/functional/eval/hostname_spec.lua deleted file mode 100644 index 6112cf64e3..0000000000 --- a/test/functional/eval/hostname_spec.lua +++ /dev/null @@ -1,20 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq -local ok = helpers.ok -local call = helpers.call -local clear = helpers.clear -local iswin = helpers.iswin - -describe('hostname()', function() - before_each(clear) - - it('returns hostname string', function() - local actual = call('hostname') - ok(string.len(actual) > 0) - if call('executable', 'hostname') == 1 then - local expected = string.gsub(call('system', 'hostname'), '[\n\r]', '') - eq((iswin() and expected:upper() or expected), - (iswin() and actual:upper() or actual)) - end - end) -end) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua deleted file mode 100644 index 14c02f9eb2..0000000000 --- a/test/functional/eval/input_spec.lua +++ /dev/null @@ -1,483 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local Screen = require('test.functional.ui.screen') - -local eq = helpers.eq -local feed = helpers.feed -local meths = helpers.meths -local clear = helpers.clear -local source = helpers.source -local command = helpers.command -local exc_exec = helpers.exc_exec -local nvim_async = helpers.nvim_async - -local screen - -before_each(function() - clear() - screen = Screen.new(25, 5) - screen:attach() - source([[ - hi Test ctermfg=Red guifg=Red term=bold - function CustomCompl(...) - return 'TEST' - endfunction - function CustomListCompl(...) - return ['FOO'] - endfunction - - highlight RBP1 guibg=Red - highlight RBP2 guibg=Yellow - highlight RBP3 guibg=Green - highlight RBP4 guibg=Blue - let g:NUM_LVLS = 4 - function Redraw() - redraw! - return '' - endfunction - cnoremap {REDRAW} Redraw() - function RainBowParens(cmdline) - let ret = [] - let i = 0 - let lvl = 0 - while i < len(a:cmdline) - if a:cmdline[i] is# '(' - call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)]) - let lvl += 1 - elseif a:cmdline[i] is# ')' - let lvl -= 1 - call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)]) - endif - let i += 1 - endwhile - return ret - endfunction - ]]) - screen:set_default_attr_ids({ - EOB={bold = true, foreground = Screen.colors.Blue1}, - T={foreground=Screen.colors.Red}, - RBP1={background=Screen.colors.Red}, - RBP2={background=Screen.colors.Yellow}, - RBP3={background=Screen.colors.Green}, - RBP4={background=Screen.colors.Blue}, - SEP={bold = true, reverse = true}, - CONFIRM={bold = true, foreground = Screen.colors.SeaGreen4}, - }) -end) - -describe('input()', function() - it('works with multiline prompts', function() - feed([[:call input("Test\nFoo")]]) - screen:expect([[ - | - {EOB:~ }| - {SEP: }| - Test | - Foo^ | - ]]) - end) - it('works with multiline prompts and :echohl', function() - feed([[:echohl Test | call input("Test\nFoo")]]) - screen:expect([[ - | - {EOB:~ }| - {SEP: }| - {T:Test} | - {T:Foo}^ | - ]]) - command('redraw!') - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:Foo}^ | - ]]) - end) - it('allows unequal numeric arguments when using multiple args', function() - command('echohl Test') - feed([[:call input(1, 2)]]) - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:1}2^ | - ]]) - feed('') - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:1}^ | - ]]) - end) - it('allows unequal numeric values when using {opts} dictionary', function() - command('echohl Test') - meths.set_var('opts', {prompt=1, default=2, cancelreturn=3}) - feed([[:echo input(opts)]]) - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:1}2^ | - ]]) - feed('') - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:1}^ | - ]]) - feed('') - screen:expect([[ - ^ | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:3} | - ]]) - end) - it('works with redraw', function() - command('echohl Test') - meths.set_var('opts', {prompt='Foo>', default='Bar'}) - feed([[:echo inputdialog(opts)]]) - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:Foo>}Bar^ | - ]]) - command('mode') - screen:expect{grid=[[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:Foo>}Bar^ | - ]], reset=true} - feed('') - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:Foo>}Ba^ | - ]]) - command('mode') - screen:expect{grid=[[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:Foo>}Ba^ | - ]], reset=true} - end) - it('allows omitting everything with dictionary argument', function() - command('echohl Test') - feed([[:call input({})]]) - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - ^ | - ]]) - end) - it('supports completion', function() - feed(':let var = input("", "", "custom,CustomCompl")') - feed('') - eq('TEST', meths.get_var('var')) - - feed(':let var = input({"completion": "customlist,CustomListCompl"})') - feed('') - eq('FOO', meths.get_var('var')) - end) - it('supports cancelreturn', function() - feed(':let var = input({"cancelreturn": "BAR"})') - feed('') - eq('BAR', meths.get_var('var')) - end) - it('supports default string', function() - feed(':let var = input("", "DEF1")') - feed('') - eq('DEF1', meths.get_var('var')) - - feed(':let var = input({"default": "DEF2"})') - feed('') - eq('DEF2', meths.get_var('var')) - end) - it('errors out on invalid inputs', function() - eq('Vim(call):E730: using List as a String', - exc_exec('call input([])')) - eq('Vim(call):E730: using List as a String', - exc_exec('call input("", [])')) - eq('Vim(call):E730: using List as a String', - exc_exec('call input("", "", [])')) - eq('Vim(call):E730: using List as a String', - exc_exec('call input({"prompt": []})')) - eq('Vim(call):E730: using List as a String', - exc_exec('call input({"cancelreturn": []})')) - eq('Vim(call):E730: using List as a String', - exc_exec('call input({"default": []})')) - eq('Vim(call):E730: using List as a String', - exc_exec('call input({"completion": []})')) - eq('Vim(call):E5050: {opts} must be the only argument', - exc_exec('call input({}, "default")')) - eq('Vim(call):E118: Too many arguments for function: input', - exc_exec('call input("prompt> ", "default", "file", "extra")')) - end) - it('supports highlighting', function() - command('nnoremap X input({"highlight": "RainBowParens"})[-1]') - feed([[X]]) - feed('(())') - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {RBP1:(}{RBP2:()}{RBP1:)}^ | - ]]) - end) - it('is not hidden by :silent', function() - feed([[:silent call input('Foo: ')]]) - screen:expect([[ - | - {EOB:~ }| - {SEP: }| - Foo: ^ | - | - ]]) - feed('Bar') - screen:expect([[ - | - {EOB:~ }| - {SEP: }| - Foo: Bar^ | - | - ]]) - feed('') - end) -end) -describe('inputdialog()', function() - it('works with multiline prompts', function() - feed([[:call inputdialog("Test\nFoo")]]) - screen:expect([[ - | - {EOB:~ }| - {SEP: }| - Test | - Foo^ | - ]]) - end) - it('works with multiline prompts and :echohl', function() - feed([[:echohl Test | call inputdialog("Test\nFoo")]]) - screen:expect([[ - | - {EOB:~ }| - {SEP: }| - {T:Test} | - {T:Foo}^ | - ]]) - command('redraw!') - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:Foo}^ | - ]]) - end) - it('allows unequal numeric arguments when using multiple args', function() - command('echohl Test') - feed([[:call inputdialog(1, 2)]]) - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:1}2^ | - ]]) - feed('') - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:1}^ | - ]]) - end) - it('allows unequal numeric values when using {opts} dictionary', function() - command('echohl Test') - meths.set_var('opts', {prompt=1, default=2, cancelreturn=3}) - feed([[:echo input(opts)]]) - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:1}2^ | - ]]) - feed('') - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:1}^ | - ]]) - feed('') - screen:expect([[ - ^ | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:3} | - ]]) - end) - it('works with redraw', function() - command('echohl Test') - meths.set_var('opts', {prompt='Foo>', default='Bar'}) - feed([[:echo input(opts)]]) - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:Foo>}Bar^ | - ]]) - command('mode') - screen:expect{grid=[[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:Foo>}Bar^ | - ]], reset=true} - feed('') - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:Foo>}Ba^ | - ]]) - command('mode') - screen:expect{grid=[[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {T:Foo>}Ba^ | - ]], reset=true} - end) - it('allows omitting everything with dictionary argument', function() - command('echohl Test') - feed(':echo inputdialog({})') - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - ^ | - ]]) - end) - it('supports completion', function() - feed(':let var = inputdialog({"completion": "customlist,CustomListCompl"})') - feed('') - eq('FOO', meths.get_var('var')) - end) - it('supports cancelreturn', function() - feed(':let var = inputdialog("", "", "CR1")') - feed('') - eq('CR1', meths.get_var('var')) - - feed(':let var = inputdialog({"cancelreturn": "BAR"})') - feed('') - eq('BAR', meths.get_var('var')) - end) - it('supports default string', function() - feed(':let var = inputdialog("", "DEF1")') - feed('') - eq('DEF1', meths.get_var('var')) - - feed(':let var = inputdialog({"default": "DEF2"})') - feed('') - eq('DEF2', meths.get_var('var')) - end) - it('errors out on invalid inputs', function() - eq('Vim(call):E730: using List as a String', - exc_exec('call inputdialog([])')) - eq('Vim(call):E730: using List as a String', - exc_exec('call inputdialog("", [])')) - eq('Vim(call):E730: using List as a String', - exc_exec('call inputdialog("", "", [])')) - eq('Vim(call):E730: using List as a String', - exc_exec('call inputdialog({"prompt": []})')) - eq('Vim(call):E730: using List as a String', - exc_exec('call inputdialog({"cancelreturn": []})')) - eq('Vim(call):E730: using List as a String', - exc_exec('call inputdialog({"default": []})')) - eq('Vim(call):E730: using List as a String', - exc_exec('call inputdialog({"completion": []})')) - eq('Vim(call):E5050: {opts} must be the only argument', - exc_exec('call inputdialog({}, "default")')) - eq('Vim(call):E118: Too many arguments for function: inputdialog', - exc_exec('call inputdialog("prompt> ", "default", "file", "extra")')) - end) - it('supports highlighting', function() - command('nnoremap X inputdialog({"highlight": "RainBowParens"})[-1]') - feed([[X]]) - feed('(())') - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {RBP1:(}{RBP2:()}{RBP1:)}^ | - ]]) - end) -end) - -describe('confirm()', function() - it("shows dialog even if :silent #8788", function() - command("autocmd BufNewFile * call confirm('test')") - - local function check_and_clear(edit_line) - screen:expect([[ - | - {SEP: }| - ]]..edit_line..[[ - {CONFIRM:test} | - {CONFIRM:[O]k: }^ | - ]]) - feed('') - command('redraw') - command('bdelete!') - end - - -- With shortmess-=F - command('set shortmess-=F') - feed(':edit foo') - check_and_clear('"foo" [New] |\n') - - -- With shortmess+=F - command('set shortmess+=F') - feed(':edit foo') - check_and_clear(':edit foo |\n') - - -- With :silent - feed(':silent edit foo') - check_and_clear(':silent edit foo |\n') - - -- With API (via eval/VimL) call and shortmess+=F - feed(':call nvim_command("edit x")') - check_and_clear(':call nvim_command("edit |\n') - - nvim_async('command', 'edit x') - check_and_clear(' |\n') - end) -end) diff --git a/test/functional/eval/interrupt_spec.lua b/test/functional/eval/interrupt_spec.lua deleted file mode 100644 index 05b1f4ff57..0000000000 --- a/test/functional/eval/interrupt_spec.lua +++ /dev/null @@ -1,61 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local command = helpers.command -local meths = helpers.meths -local clear = helpers.clear -local sleep = helpers.sleep -local poke_eventloop = helpers.poke_eventloop -local feed = helpers.feed -local eq = helpers.eq - -local dur -local min_dur = 8 -local len = 131072 - -describe('List support code', function() - if not pending('does not actually allows interrupting with just got_int', function() end) then return end - -- The following tests are confirmed to work with os_breakcheck() just before - -- `if (got_int) {break;}` in tv_list_copy and list_join_inner() and not to - -- work without. - setup(function() - clear() - dur = 0 - while true do - command(([[ - let rt = reltime() - let bl = range(%u) - let dur = reltimestr(reltime(rt)) - ]]):format(len)) - dur = tonumber(meths.get_var('dur')) - if dur >= min_dur then - -- print(('Using len %u, dur %g'):format(len, dur)) - break - else - len = len * 2 - end - end - end) - it('allows interrupting copy', function() - feed(':let t_rt = reltime():let t_bl = copy(bl)') - sleep(min_dur / 16 * 1000) - feed('') - poke_eventloop() - command('let t_dur = reltimestr(reltime(t_rt))') - local t_dur = tonumber(meths.get_var('t_dur')) - if t_dur >= dur / 8 then - eq(nil, ('Took too long to cancel: %g >= %g'):format(t_dur, dur / 8)) - end - end) - it('allows interrupting join', function() - feed(':let t_rt = reltime():let t_j = join(bl)') - sleep(min_dur / 16 * 1000) - feed('') - poke_eventloop() - command('let t_dur = reltimestr(reltime(t_rt))') - local t_dur = tonumber(meths.get_var('t_dur')) - print(('t_dur: %g'):format(t_dur)) - if t_dur >= dur / 8 then - eq(nil, ('Took too long to cancel: %g >= %g'):format(t_dur, dur / 8)) - end - end) -end) diff --git a/test/functional/eval/json_functions_spec.lua b/test/functional/eval/json_functions_spec.lua deleted file mode 100644 index 9b5e207c07..0000000000 --- a/test/functional/eval/json_functions_spec.lua +++ /dev/null @@ -1,795 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local funcs = helpers.funcs -local meths = helpers.meths -local eq = helpers.eq -local eval = helpers.eval -local command = helpers.command -local exc_exec = helpers.exc_exec -local redir_exec = helpers.redir_exec -local NIL = helpers.NIL -local source = helpers.source - -describe('json_decode() function', function() - local restart = function(...) - clear(...) - source([[ - language C - function Eq(exp, act) - let act = a:act - let exp = a:exp - if type(exp) != type(act) - return 0 - endif - if type(exp) == type({}) - if sort(keys(exp)) !=# sort(keys(act)) - return 0 - endif - if sort(keys(exp)) ==# ['_TYPE', '_VAL'] - let exp_typ = v:msgpack_types[exp._TYPE] - let act_typ = act._TYPE - if exp_typ isnot act_typ - return 0 - endif - return Eq(exp._VAL, act._VAL) - else - return empty(filter(copy(exp), '!Eq(v:val, act[v:key])')) - endif - else - if type(exp) == type([]) - if len(exp) != len(act) - return 0 - endif - return empty(filter(copy(exp), '!Eq(v:val, act[v:key])')) - endif - return exp ==# act - endif - return 1 - endfunction - function EvalEq(exp, act_expr) - let act = eval(a:act_expr) - if Eq(a:exp, act) - return 1 - else - return string(act) - endif - endfunction - ]]) - end - before_each(restart) - - local speq = function(expected, actual_expr) - eq(1, funcs.EvalEq(expected, actual_expr)) - end - - it('accepts readfile()-style list', function() - eq({Test=1}, funcs.json_decode({ - '{', - '\t"Test": 1', - '}', - })) - end) - - it('accepts strings with newlines', function() - eq({Test=1}, funcs.json_decode([[ - { - "Test": 1 - } - ]])) - end) - - it('parses null, true, false', function() - eq(NIL, funcs.json_decode('null')) - eq(true, funcs.json_decode('true')) - eq(false, funcs.json_decode('false')) - end) - - it('fails to parse incomplete null, true, false', function() - eq('Vim(call):E474: Expected null: n', - exc_exec('call json_decode("n")')) - eq('Vim(call):E474: Expected null: nu', - exc_exec('call json_decode("nu")')) - eq('Vim(call):E474: Expected null: nul', - exc_exec('call json_decode("nul")')) - eq('Vim(call):E474: Expected null: nul\n\t', - exc_exec('call json_decode("nul\\n\\t")')) - - eq('Vim(call):E474: Expected true: t', - exc_exec('call json_decode("t")')) - eq('Vim(call):E474: Expected true: tr', - exc_exec('call json_decode("tr")')) - eq('Vim(call):E474: Expected true: tru', - exc_exec('call json_decode("tru")')) - eq('Vim(call):E474: Expected true: tru\t\n', - exc_exec('call json_decode("tru\\t\\n")')) - - eq('Vim(call):E474: Expected false: f', - exc_exec('call json_decode("f")')) - eq('Vim(call):E474: Expected false: fa', - exc_exec('call json_decode("fa")')) - eq('Vim(call):E474: Expected false: fal', - exc_exec('call json_decode("fal")')) - eq('Vim(call):E474: Expected false: fal <', - exc_exec('call json_decode(" fal <")')) - eq('Vim(call):E474: Expected false: fals', - exc_exec('call json_decode("fals")')) - end) - - it('parses integer numbers', function() - eq(100000, funcs.json_decode('100000')) - eq(-100000, funcs.json_decode('-100000')) - eq(100000, funcs.json_decode(' 100000 ')) - eq(-100000, funcs.json_decode(' -100000 ')) - eq(0, funcs.json_decode('0')) - eq(0, funcs.json_decode('-0')) - end) - - it('fails to parse +numbers and .number', function() - eq('Vim(call):E474: Unidentified byte: +1000', - exc_exec('call json_decode("+1000")')) - eq('Vim(call):E474: Unidentified byte: .1000', - exc_exec('call json_decode(".1000")')) - end) - - it('fails to parse numbers with leading zeroes', function() - eq('Vim(call):E474: Leading zeroes are not allowed: 00.1', - exc_exec('call json_decode("00.1")')) - eq('Vim(call):E474: Leading zeroes are not allowed: 01', - exc_exec('call json_decode("01")')) - eq('Vim(call):E474: Leading zeroes are not allowed: -01', - exc_exec('call json_decode("-01")')) - eq('Vim(call):E474: Leading zeroes are not allowed: -001.0', - exc_exec('call json_decode("-001.0")')) - end) - - it('fails to parse incomplete numbers', function() - eq('Vim(call):E474: Missing number after minus sign: -.1', - exc_exec('call json_decode("-.1")')) - eq('Vim(call):E474: Missing number after minus sign: -', - exc_exec('call json_decode("-")')) - eq('Vim(call):E474: Missing number after decimal dot: -1.', - exc_exec('call json_decode("-1.")')) - eq('Vim(call):E474: Missing number after decimal dot: 0.', - exc_exec('call json_decode("0.")')) - eq('Vim(call):E474: Missing exponent: 0.0e', - exc_exec('call json_decode("0.0e")')) - eq('Vim(call):E474: Missing exponent: 0.0e+', - exc_exec('call json_decode("0.0e+")')) - eq('Vim(call):E474: Missing exponent: 0.0e-', - exc_exec('call json_decode("0.0e-")')) - eq('Vim(call):E474: Missing exponent: 0.0e-', - exc_exec('call json_decode("0.0e-")')) - eq('Vim(call):E474: Missing number after decimal dot: 1.e5', - exc_exec('call json_decode("1.e5")')) - eq('Vim(call):E474: Missing number after decimal dot: 1.e+5', - exc_exec('call json_decode("1.e+5")')) - eq('Vim(call):E474: Missing number after decimal dot: 1.e+', - exc_exec('call json_decode("1.e+")')) - end) - - it('parses floating-point numbers', function() - eq('100000.0', eval('string(json_decode("100000.0"))')) - eq(100000.5, funcs.json_decode('100000.5')) - eq(-100000.5, funcs.json_decode('-100000.5')) - eq(-100000.5e50, funcs.json_decode('-100000.5e50')) - eq(100000.5e50, funcs.json_decode('100000.5e50')) - eq(100000.5e50, funcs.json_decode('100000.5e+50')) - eq(-100000.5e-50, funcs.json_decode('-100000.5e-50')) - eq(100000.5e-50, funcs.json_decode('100000.5e-50')) - eq(100000e-50, funcs.json_decode('100000e-50')) - eq(0.5, funcs.json_decode('0.5')) - eq(0.005, funcs.json_decode('0.005')) - eq(0.005, funcs.json_decode('0.00500')) - eq(0.5, funcs.json_decode('0.00500e+002')) - eq(0.00005, funcs.json_decode('0.00500e-002')) - - eq(-0.0, funcs.json_decode('-0.0')) - eq(-0.0, funcs.json_decode('-0.0e0')) - eq(-0.0, funcs.json_decode('-0.0e+0')) - eq(-0.0, funcs.json_decode('-0.0e-0')) - eq(-0.0, funcs.json_decode('-0e-0')) - eq(-0.0, funcs.json_decode('-0e-2')) - eq(-0.0, funcs.json_decode('-0e+2')) - - eq(0.0, funcs.json_decode('0.0')) - eq(0.0, funcs.json_decode('0.0e0')) - eq(0.0, funcs.json_decode('0.0e+0')) - eq(0.0, funcs.json_decode('0.0e-0')) - eq(0.0, funcs.json_decode('0e-0')) - eq(0.0, funcs.json_decode('0e-2')) - eq(0.0, funcs.json_decode('0e+2')) - end) - - it('fails to parse numbers with spaces inside', function() - eq('Vim(call):E474: Missing number after minus sign: - 1000', - exc_exec('call json_decode("- 1000")')) - eq('Vim(call):E474: Missing number after decimal dot: 0. ', - exc_exec('call json_decode("0. ")')) - eq('Vim(call):E474: Missing number after decimal dot: 0. 0', - exc_exec('call json_decode("0. 0")')) - eq('Vim(call):E474: Missing exponent: 0.0e 1', - exc_exec('call json_decode("0.0e 1")')) - eq('Vim(call):E474: Missing exponent: 0.0e+ 1', - exc_exec('call json_decode("0.0e+ 1")')) - eq('Vim(call):E474: Missing exponent: 0.0e- 1', - exc_exec('call json_decode("0.0e- 1")')) - end) - - it('fails to parse "," and ":"', function() - eq('Vim(call):E474: Comma not inside container: , ', - exc_exec('call json_decode(" , ")')) - eq('Vim(call):E474: Colon not inside container: : ', - exc_exec('call json_decode(" : ")')) - end) - - it('parses empty containers', function() - eq({}, funcs.json_decode('[]')) - eq('[]', eval('string(json_decode("[]"))')) - end) - - it('fails to parse "[" and "{"', function() - eq('Vim(call):E474: Unexpected end of input: {', - exc_exec('call json_decode("{")')) - eq('Vim(call):E474: Unexpected end of input: [', - exc_exec('call json_decode("[")')) - end) - - it('fails to parse "}" and "]"', function() - eq('Vim(call):E474: No container to close: ]', - exc_exec('call json_decode("]")')) - eq('Vim(call):E474: No container to close: }', - exc_exec('call json_decode("}")')) - end) - - it('fails to parse containers which are closed by different brackets', - function() - eq('Vim(call):E474: Closing dictionary with square bracket: ]', - exc_exec('call json_decode("{]")')) - eq('Vim(call):E474: Closing list with curly bracket: }', - exc_exec('call json_decode("[}")')) - end) - - it('fails to parse concat inside container', function() - eq('Vim(call):E474: Expected comma before list item: []]', - exc_exec('call json_decode("[[][]]")')) - eq('Vim(call):E474: Expected comma before list item: {}]', - exc_exec('call json_decode("[{}{}]")')) - eq('Vim(call):E474: Expected comma before list item: ]', - exc_exec('call json_decode("[1 2]")')) - eq('Vim(call):E474: Expected comma before dictionary key: ": 4}', - exc_exec('call json_decode("{\\"1\\": 2 \\"3\\": 4}")')) - eq('Vim(call):E474: Expected colon before dictionary value: , "3" 4}', - exc_exec('call json_decode("{\\"1\\" 2, \\"3\\" 4}")')) - end) - - it('fails to parse containers with leading comma or colon', function() - eq('Vim(call):E474: Leading comma: ,}', - exc_exec('call json_decode("{,}")')) - eq('Vim(call):E474: Leading comma: ,]', - exc_exec('call json_decode("[,]")')) - eq('Vim(call):E474: Using colon not in dictionary: :]', - exc_exec('call json_decode("[:]")')) - eq('Vim(call):E474: Unexpected colon: :}', - exc_exec('call json_decode("{:}")')) - end) - - it('fails to parse containers with trailing comma', function() - eq('Vim(call):E474: Trailing comma: ]', - exc_exec('call json_decode("[1,]")')) - eq('Vim(call):E474: Trailing comma: }', - exc_exec('call json_decode("{\\"1\\": 2,}")')) - end) - - it('fails to parse dictionaries with missing value', function() - eq('Vim(call):E474: Expected value after colon: }', - exc_exec('call json_decode("{\\"1\\":}")')) - eq('Vim(call):E474: Expected value: }', - exc_exec('call json_decode("{\\"1\\"}")')) - end) - - it('fails to parse containers with two commas or colons', function() - eq('Vim(call):E474: Duplicate comma: , "2": 2}', - exc_exec('call json_decode("{\\"1\\": 1,, \\"2\\": 2}")')) - eq('Vim(call):E474: Duplicate comma: , "2", 2]', - exc_exec('call json_decode("[\\"1\\", 1,, \\"2\\", 2]")')) - eq('Vim(call):E474: Duplicate colon: : 2}', - exc_exec('call json_decode("{\\"1\\": 1, \\"2\\":: 2}")')) - eq('Vim(call):E474: Comma after colon: , 2}', - exc_exec('call json_decode("{\\"1\\": 1, \\"2\\":, 2}")')) - eq('Vim(call):E474: Unexpected colon: : "2": 2}', - exc_exec('call json_decode("{\\"1\\": 1,: \\"2\\": 2}")')) - eq('Vim(call):E474: Unexpected colon: :, "2": 2}', - exc_exec('call json_decode("{\\"1\\": 1:, \\"2\\": 2}")')) - end) - - it('fails to parse concat of two values', function() - eq('Vim(call):E474: Trailing characters: []', - exc_exec('call json_decode("{}[]")')) - end) - - it('parses containers', function() - eq({1}, funcs.json_decode('[1]')) - eq({NIL, 1}, funcs.json_decode('[null, 1]')) - eq({['1']=2}, funcs.json_decode('{"1": 2}')) - eq({['1']=2, ['3']={{['4']={['5']={{}, 1}}}}}, - funcs.json_decode('{"1": 2, "3": [{"4": {"5": [[], 1]}}]}')) - end) - - it('fails to parse incomplete strings', function() - eq('Vim(call):E474: Expected string end: \t"', - exc_exec('call json_decode("\\t\\"")')) - eq('Vim(call):E474: Expected string end: \t"abc', - exc_exec('call json_decode("\\t\\"abc")')) - eq('Vim(call):E474: Unfinished escape sequence: \t"abc\\', - exc_exec('call json_decode("\\t\\"abc\\\\")')) - eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u', - exc_exec('call json_decode("\\t\\"abc\\\\u")')) - eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u0', - exc_exec('call json_decode("\\t\\"abc\\\\u0")')) - eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u00', - exc_exec('call json_decode("\\t\\"abc\\\\u00")')) - eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u000', - exc_exec('call json_decode("\\t\\"abc\\\\u000")')) - eq('Vim(call):E474: Expected four hex digits after \\u: \\u" ', - exc_exec('call json_decode("\\t\\"abc\\\\u\\" ")')) - eq('Vim(call):E474: Expected four hex digits after \\u: \\u0" ', - exc_exec('call json_decode("\\t\\"abc\\\\u0\\" ")')) - eq('Vim(call):E474: Expected four hex digits after \\u: \\u00" ', - exc_exec('call json_decode("\\t\\"abc\\\\u00\\" ")')) - eq('Vim(call):E474: Expected four hex digits after \\u: \\u000" ', - exc_exec('call json_decode("\\t\\"abc\\\\u000\\" ")')) - eq('Vim(call):E474: Expected string end: \t"abc\\u0000', - exc_exec('call json_decode("\\t\\"abc\\\\u0000")')) - end) - - it('fails to parse unknown escape sequnces', function() - eq('Vim(call):E474: Unknown escape sequence: \\a"', - exc_exec('call json_decode("\\t\\"\\\\a\\"")')) - end) - - it('parses strings properly', function() - eq('\n', funcs.json_decode('"\\n"')) - eq('', funcs.json_decode('""')) - eq('\\/"\t\b\n\r\f', funcs.json_decode([["\\\/\"\t\b\n\r\f"]])) - eq('/a', funcs.json_decode([["\/a"]])) - -- Unicode characters: 2-byte, 3-byte, 4-byte - eq({ - '«', - 'ફ', - '\240\144\128\128', - }, funcs.json_decode({ - '[', - '"«",', - '"ફ",', - '"\240\144\128\128"', - ']', - })) - end) - - it('fails on strings with invalid bytes', function() - eq('Vim(call):E474: Only UTF-8 strings allowed: \255"', - exc_exec('call json_decode("\\t\\"\\xFF\\"")')) - eq('Vim(call):E474: ASCII control characters cannot be present inside string: ', - exc_exec('call json_decode(["\\"\\n\\""])')) - -- 0xC2 starts 2-byte unicode character - eq('Vim(call):E474: Only UTF-8 strings allowed: \194"', - exc_exec('call json_decode("\\t\\"\\xC2\\"")')) - -- 0xE0 0xAA starts 3-byte unicode character - eq('Vim(call):E474: Only UTF-8 strings allowed: \224"', - exc_exec('call json_decode("\\t\\"\\xE0\\"")')) - eq('Vim(call):E474: Only UTF-8 strings allowed: \224\170"', - exc_exec('call json_decode("\\t\\"\\xE0\\xAA\\"")')) - -- 0xF0 0x90 0x80 starts 4-byte unicode character - eq('Vim(call):E474: Only UTF-8 strings allowed: \240"', - exc_exec('call json_decode("\\t\\"\\xF0\\"")')) - eq('Vim(call):E474: Only UTF-8 strings allowed: \240\144"', - exc_exec('call json_decode("\\t\\"\\xF0\\x90\\"")')) - eq('Vim(call):E474: Only UTF-8 strings allowed: \240\144\128"', - exc_exec('call json_decode("\\t\\"\\xF0\\x90\\x80\\"")')) - -- 0xF9 0x80 0x80 0x80 starts 5-byte unicode character - eq('Vim(call):E474: Only UTF-8 strings allowed: \249"', - exc_exec('call json_decode("\\t\\"\\xF9\\"")')) - eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128"', - exc_exec('call json_decode("\\t\\"\\xF9\\x80\\"")')) - eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128\128"', - exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\"")')) - eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128\128\128"', - exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\x80\\"")')) - -- 0xFC 0x90 0x80 0x80 0x80 starts 6-byte unicode character - eq('Vim(call):E474: Only UTF-8 strings allowed: \252"', - exc_exec('call json_decode("\\t\\"\\xFC\\"")')) - eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144"', - exc_exec('call json_decode("\\t\\"\\xFC\\x90\\"")')) - eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128"', - exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\"")')) - eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128\128"', - exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\"")')) - eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128\128\128"', - exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\x80\\"")')) - -- Specification does not allow unquoted characters above 0x10FFFF - eq('Vim(call):E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \249\128\128\128\128"', - exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\x80\\x80\\"")')) - eq('Vim(call):E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \252\144\128\128\128\128"', - exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\x80\\x80\\"")')) - -- '"\249\128\128\128\128"', - -- '"\252\144\128\128\128\128"', - end) - - it('parses surrogate pairs properly', function() - eq('\240\144\128\128', funcs.json_decode('"\\uD800\\uDC00"')) - eq('\237\160\128a\237\176\128', funcs.json_decode('"\\uD800a\\uDC00"')) - eq('\237\160\128\t\237\176\128', funcs.json_decode('"\\uD800\\t\\uDC00"')) - - eq('\237\160\128', funcs.json_decode('"\\uD800"')) - eq('\237\160\128a', funcs.json_decode('"\\uD800a"')) - eq('\237\160\128\t', funcs.json_decode('"\\uD800\\t"')) - - eq('\237\176\128', funcs.json_decode('"\\uDC00"')) - eq('\237\176\128a', funcs.json_decode('"\\uDC00a"')) - eq('\237\176\128\t', funcs.json_decode('"\\uDC00\\t"')) - - eq('\237\176\128', funcs.json_decode('"\\uDC00"')) - eq('a\237\176\128', funcs.json_decode('"a\\uDC00"')) - eq('\t\237\176\128', funcs.json_decode('"\\t\\uDC00"')) - - eq('\237\160\128¬', funcs.json_decode('"\\uD800\\u00AC"')) - - eq('\237\160\128\237\160\128', funcs.json_decode('"\\uD800\\uD800"')) - end) - - local sp_decode_eq = function(expected, json) - meths.set_var('__json', json) - speq(expected, 'json_decode(g:__json)') - command('unlet! g:__json') - end - - it('parses strings with NUL properly', function() - sp_decode_eq({_TYPE='string', _VAL={'\n'}}, '"\\u0000"') - sp_decode_eq({_TYPE='string', _VAL={'\n', '\n'}}, '"\\u0000\\n\\u0000"') - sp_decode_eq({_TYPE='string', _VAL={'\n«\n'}}, '"\\u0000\\u00AB\\u0000"') - end) - - it('parses dictionaries with duplicate keys to special maps', function() - sp_decode_eq({_TYPE='map', _VAL={{'a', 1}, {'a', 2}}}, - '{"a": 1, "a": 2}') - sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'a', 2}}}, - '{"b": 3, "a": 1, "a": 2}') - sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}}}, - '{"b": 3, "a": 1, "c": 4, "a": 2}') - sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}, - '{"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}') - sp_decode_eq({{_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}, - '[{"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}]') - sp_decode_eq({{d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}}, - '[{"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]') - sp_decode_eq({1, {d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}}, - '[1, {"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]') - sp_decode_eq({1, {a={}, d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}}, - '[1, {"a": [], "d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]') - end) - - it('parses dictionaries with empty keys to special maps', function() - sp_decode_eq({_TYPE='map', _VAL={{'', 4}}}, - '{"": 4}') - sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}}, - '{"b": 3, "a": 1, "c": 4, "d": 2, "": 4}') - sp_decode_eq({_TYPE='map', _VAL={{'', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}}, - '{"": 3, "a": 1, "c": 4, "d": 2, "": 4}') - sp_decode_eq({{_TYPE='map', _VAL={{'', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}}}, - '[{"": 3, "a": 1, "c": 4, "d": 2, "": 4}]') - end) - - it('parses dictionaries with keys with NUL bytes to special maps', function() - sp_decode_eq({_TYPE='map', _VAL={{{_TYPE='string', _VAL={'a\n', 'b'}}, 4}}}, - '{"a\\u0000\\nb": 4}') - sp_decode_eq({_TYPE='map', _VAL={{{_TYPE='string', _VAL={'a\n', 'b', ''}}, 4}}}, - '{"a\\u0000\\nb\\n": 4}') - sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {{_TYPE='string', _VAL={'\n'}}, 4}}}, - '{"b": 3, "a": 1, "c": 4, "d": 2, "\\u0000": 4}') - end) - - it('parses U+00C3 correctly', function() - eq('\195\131', funcs.json_decode('"\195\131"')) - end) - - it('fails to parse empty string', function() - eq('Vim(call):E474: Attempt to decode a blank string', - exc_exec('call json_decode("")')) - eq('Vim(call):E474: Attempt to decode a blank string', - exc_exec('call json_decode([])')) - eq('Vim(call):E474: Attempt to decode a blank string', - exc_exec('call json_decode([""])')) - eq('Vim(call):E474: Attempt to decode a blank string', - exc_exec('call json_decode(" ")')) - eq('Vim(call):E474: Attempt to decode a blank string', - exc_exec('call json_decode("\\t")')) - eq('Vim(call):E474: Attempt to decode a blank string', - exc_exec('call json_decode("\\n")')) - eq('Vim(call):E474: Attempt to decode a blank string', - exc_exec('call json_decode(" \\t\\n \\n\\t\\t \\n\\t\\n \\n \\t\\n\\t ")')) - end) - - it('accepts all spaces in every position where space may be put', function() - local s = ' \t\n\r \t\r\n \n\t\r \n\r\t \r\t\n \r\n\t\t \n\r\t \r\n\t\n \r\t\n\r \t\r \n\t\r\n \n \t\r\n \r\t\n\t \r\n\t\r \n\r \t\n\r\t \r \t\n\r \n\t\r\t \n\r\t\n \r\n \t\r\n\t' - local str = ('%s{%s"key"%s:%s[%s"val"%s,%s"val2"%s]%s,%s"key2"%s:%s1%s}%s'):gsub('%%s', s) - eq({key={'val', 'val2'}, key2=1}, funcs.json_decode(str)) - end) - - it('does not overflow when writing error message about decoding ["", ""]', - function() - eq('\nE474: Attempt to decode a blank string' - .. '\nE474: Failed to parse \n', - redir_exec('call json_decode(["", ""])')) - end) -end) - -describe('json_encode() function', function() - before_each(function() - clear() - command('language C') - end) - - it('dumps strings', function() - eq('"Test"', funcs.json_encode('Test')) - eq('""', funcs.json_encode('')) - eq('"\\t"', funcs.json_encode('\t')) - eq('"\\n"', funcs.json_encode('\n')) - eq('"\\u001B"', funcs.json_encode('\27')) - eq('"þÿþ"', funcs.json_encode('þÿþ')) - end) - - it('dumps blobs', function() - eq('[]', eval('json_encode(0z)')) - eq('[222, 173, 190, 239]', eval('json_encode(0zDEADBEEF)')) - end) - - it('dumps numbers', function() - eq('0', funcs.json_encode(0)) - eq('10', funcs.json_encode(10)) - eq('-10', funcs.json_encode(-10)) - end) - - it('dumps floats', function() - eq('0.0', eval('json_encode(0.0)')) - eq('10.5', funcs.json_encode(10.5)) - eq('-10.5', funcs.json_encode(-10.5)) - eq('-1.0e-5', funcs.json_encode(-1e-5)) - eq('1.0e50', eval('json_encode(1.0e50)')) - end) - - it('fails to dump NaN and infinite values', function() - eq('Vim(call):E474: Unable to represent NaN value in JSON', - exc_exec('call json_encode(str2float("nan"))')) - eq('Vim(call):E474: Unable to represent infinity in JSON', - exc_exec('call json_encode(str2float("inf"))')) - eq('Vim(call):E474: Unable to represent infinity in JSON', - exc_exec('call json_encode(-str2float("inf"))')) - end) - - it('dumps lists', function() - eq('[]', funcs.json_encode({})) - eq('[[]]', funcs.json_encode({{}})) - eq('[[], []]', funcs.json_encode({{}, {}})) - end) - - it('dumps dictionaries', function() - eq('{}', eval('json_encode({})')) - eq('{"d": []}', funcs.json_encode({d={}})) - eq('{"d": [], "e": []}', funcs.json_encode({d={}, e={}})) - end) - - it('cannot dump generic mapping with generic mapping keys and values', - function() - command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') - command('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') - command('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') - command('call add(todump._VAL, [todumpv1, todumpv2])') - eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) - end) - - it('cannot dump generic mapping with ext key', function() - command('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') - command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') - eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) - end) - - it('cannot dump generic mapping with array key', function() - command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') - command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') - eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) - end) - - it('cannot dump generic mapping with UINT64_MAX key', function() - command('let todump = {"_TYPE": v:msgpack_types.integer}') - command('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') - command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') - eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) - end) - - it('cannot dump generic mapping with floating-point key', function() - command('let todump = {"_TYPE": v:msgpack_types.float, "_VAL": 0.125}') - command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') - eq('Vim(call):E474: Invalid key in special dictionary', exc_exec('call json_encode(todump)')) - end) - - it('can dump generic mapping with STR special key and NUL', function() - command('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n"]}') - command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') - eq('{"\\u0000": 1}', eval('json_encode(todump)')) - end) - - it('can dump generic mapping with BIN special key and NUL', function() - command('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n"]}') - command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[todump, 1]]}') - eq('{"\\u0000": 1}', eval('json_encode(todump)')) - end) - - it('can dump STR special mapping with NUL and NL', function() - command('let todump = {"_TYPE": v:msgpack_types.string, "_VAL": ["\\n", ""]}') - eq('"\\u0000\\n"', eval('json_encode(todump)')) - end) - - it('can dump BIN special mapping with NUL and NL', function() - command('let todump = {"_TYPE": v:msgpack_types.binary, "_VAL": ["\\n", ""]}') - eq('"\\u0000\\n"', eval('json_encode(todump)')) - end) - - it('cannot dump special ext mapping', function() - command('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') - eq('Vim(call):E474: Unable to convert EXT string to JSON', exc_exec('call json_encode(todump)')) - end) - - it('can dump special array mapping', function() - command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') - eq('[5, [""]]', eval('json_encode(todump)')) - end) - - it('can dump special UINT64_MAX mapping', function() - command('let todump = {"_TYPE": v:msgpack_types.integer}') - command('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') - eq('18446744073709551615', eval('json_encode(todump)')) - end) - - it('can dump special INT64_MIN mapping', function() - command('let todump = {"_TYPE": v:msgpack_types.integer}') - command('let todump._VAL = [-1, 2, 0, 0]') - eq('-9223372036854775808', eval('json_encode(todump)')) - end) - - it('can dump special BOOLEAN true mapping', function() - command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}') - eq('true', eval('json_encode(todump)')) - end) - - it('can dump special BOOLEAN false mapping', function() - command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}') - eq('false', eval('json_encode(todump)')) - end) - - it('can dump special NIL mapping', function() - command('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}') - eq('null', eval('json_encode(todump)')) - end) - - it('fails to dump a function reference', function() - eq('Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference', - exc_exec('call json_encode(function("tr"))')) - end) - - it('fails to dump a partial', function() - command('function T() dict\nendfunction') - eq('Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference', - exc_exec('call json_encode(function("T", [1, 2], {}))')) - end) - - it('fails to dump a function reference in a list', function() - eq('Vim(call):E474: Error while dumping encode_tv2json() argument, index 0: attempt to dump function reference', - exc_exec('call json_encode([function("tr")])')) - end) - - it('fails to dump a recursive list', function() - command('let todump = [[[]]]') - command('call add(todump[0][0], todump)') - eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', - exc_exec('call json_encode(todump)')) - end) - - it('fails to dump a recursive dict', function() - command('let todump = {"d": {"d": {}}}') - command('call extend(todump.d.d, {"d": todump})') - eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', - exc_exec('call json_encode([todump])')) - end) - - it('can dump dict with two same dicts inside', function() - command('let inter = {}') - command('let todump = {"a": inter, "b": inter}') - eq('{"a": {}, "b": {}}', eval('json_encode(todump)')) - end) - - it('can dump list with two same lists inside', function() - command('let inter = []') - command('let todump = [inter, inter]') - eq('[[], []]', eval('json_encode(todump)')) - end) - - it('fails to dump a recursive list in a special dict', function() - command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') - command('call add(todump._VAL, todump)') - eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', - exc_exec('call json_encode(todump)')) - end) - - it('fails to dump a recursive (val) map in a special dict', function() - command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') - command('call add(todump._VAL, ["", todump])') - eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', - exc_exec('call json_encode([todump])')) - end) - - it('fails to dump a recursive (val) map in a special dict, _VAL reference', function() - command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [["", []]]}') - command('call add(todump._VAL[0][1], todump._VAL)') - eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', - exc_exec('call json_encode(todump)')) - end) - - it('fails to dump a recursive (val) special list in a special dict', - function() - command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}') - command('call add(todump._VAL, ["", todump._VAL])') - eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', - exc_exec('call json_encode(todump)')) - end) - - it('fails when called with no arguments', function() - eq('Vim(call):E119: Not enough arguments for function: json_encode', - exc_exec('call json_encode()')) - end) - - it('fails when called with two arguments', function() - eq('Vim(call):E118: Too many arguments for function: json_encode', - exc_exec('call json_encode(["", ""], 1)')) - end) - - it('ignores improper values in &isprint', function() - meths.set_option('isprint', '1') - eq(1, eval('"\1" =~# "\\\\p"')) - eq('"\\u0001"', funcs.json_encode('\1')) - end) - - it('fails when using surrogate character in a UTF-8 string', function() - eq('Vim(call):E474: UTF-8 string contains code point which belongs to a surrogate pair: \237\160\128', - exc_exec('call json_encode("\237\160\128")')) - eq('Vim(call):E474: UTF-8 string contains code point which belongs to a surrogate pair: \237\175\191', - exc_exec('call json_encode("\237\175\191")')) - end) - - it('dumps control characters as expected', function() - eq([["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000B\f\r\u000E\u000F\u0010\u0011\u0012\u0013"]], - eval('json_encode({"_TYPE": v:msgpack_types.string, "_VAL": ["\n\1\2\3\4\5\6\7\8\9", "\11\12\13\14\15\16\17\18\19"]})')) - end) - - it('can dump NULL string', function() - eq('""', eval('json_encode($XXX_UNEXISTENT_VAR_XXX)')) - end) - - it('can dump NULL blob', function() - eq('[]', eval('json_encode(v:_null_blob)')) - end) - - it('can dump NULL list', function() - eq('[]', eval('json_encode(v:_null_list)')) - end) - - it('can dump NULL dictionary', function() - eq('{}', eval('json_encode(v:_null_dict)')) - end) - - it('fails to parse NULL strings and lists', function() - eq('Vim(call):E474: Attempt to decode a blank string', - exc_exec('call json_decode($XXX_UNEXISTENT_VAR_XXX)')) - eq('Vim(call):E474: Attempt to decode a blank string', - exc_exec('call json_decode(v:_null_list)')) - end) -end) diff --git a/test/functional/eval/let_spec.lua b/test/functional/eval/let_spec.lua deleted file mode 100644 index 5bc703b567..0000000000 --- a/test/functional/eval/let_spec.lua +++ /dev/null @@ -1,93 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local eq = helpers.eq -local clear = helpers.clear -local command = helpers.command -local eval = helpers.eval -local meths = helpers.meths -local redir_exec = helpers.redir_exec -local source = helpers.source -local nvim_dir = helpers.nvim_dir - -before_each(clear) - -describe(':let', function() - it('correctly lists variables with curly-braces', function() - meths.set_var('v', {0}) - eq('\nv [0]', redir_exec('let {"v"}')) - end) - - it('correctly lists variables with subscript', function() - meths.set_var('v', {0}) - eq('\nv[0] #0', redir_exec('let v[0]')) - eq('\ng:["v"][0] #0', redir_exec('let g:["v"][0]')) - eq('\n{"g:"}["v"][0] #0', redir_exec('let {"g:"}["v"][0]')) - end) - - it(":unlet self-referencing node in a List graph #6070", function() - -- :unlet-ing a self-referencing List must not allow GC on indirectly - -- referenced in-scope Lists. Before #6070 this caused use-after-free. - source([=[ - let [l1, l2] = [[], []] - echo 'l1:' . id(l1) - echo 'l2:' . id(l2) - echo '' - let [l3, l4] = [[], []] - call add(l4, l4) - call add(l4, l3) - call add(l3, 1) - call add(l2, l2) - call add(l2, l1) - call add(l1, 1) - unlet l2 - unlet l4 - call garbagecollect(1) - call feedkeys(":\e:echo l1 l3\n:echo 42\n:cq\n", "t") - ]=]) - end) - - it("multibyte env var #8398 #9267", function() - command("let $NVIM_TEST = 'AìaB'") - eq('AìaB', eval('$NVIM_TEST')) - command("let $NVIM_TEST = 'AaあB'") - eq('AaあB', eval('$NVIM_TEST')) - local mbyte = [[\p* .ม .ม .ม .ม่ .ม่ .ม่ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ ֹֻ - .ֹֻ .ֹֻ .ֹֻ ֹֻ ֹֻ ֹֻ .ֹֻ .ֹֻ .ֹֻ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ - .ֹֻ .ֹֻ .ֹֻ a a a ca ca ca à à à]] - command("let $NVIM_TEST = '"..mbyte.."'") - eq(mbyte, eval('$NVIM_TEST')) - end) - - it("multibyte env var to child process #8398 #9267", function() - local cmd_get_child_env = "let g:env_from_child = system(['"..nvim_dir.."/printenv-test', 'NVIM_TEST'])" - command("let $NVIM_TEST = 'AìaB'") - command(cmd_get_child_env) - eq(eval('$NVIM_TEST'), eval('g:env_from_child')) - - command("let $NVIM_TEST = 'AaあB'") - command(cmd_get_child_env) - eq(eval('$NVIM_TEST'), eval('g:env_from_child')) - - local mbyte = [[\p* .ม .ม .ม .ม่ .ม่ .ม่ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ ֹֻ - .ֹֻ .ֹֻ .ֹֻ ֹֻ ֹֻ ֹֻ .ֹֻ .ֹֻ .ֹֻ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ - .ֹֻ .ֹֻ .ֹֻ a a a ca ca ca à à à]] - command("let $NVIM_TEST = '"..mbyte.."'") - command(cmd_get_child_env) - eq(eval('$NVIM_TEST'), eval('g:env_from_child')) - end) - - it("release of list assigned to l: variable does not trigger assertion #12387, #12430", function() - source([[ - func! s:f() - let l:x = [1] - let g:x = l: - endfunc - for _ in range(2) - call s:f() - endfor - call garbagecollect() - call feedkeys('i', 't') - ]]) - eq(1, eval('1')) - end) -end) diff --git a/test/functional/eval/map_functions_spec.lua b/test/functional/eval/map_functions_spec.lua deleted file mode 100644 index 275c72d212..0000000000 --- a/test/functional/eval/map_functions_spec.lua +++ /dev/null @@ -1,163 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local funcs = helpers.funcs -local nvim = helpers.nvim -local source = helpers.source -local command = helpers.command - -describe('maparg()', function() - before_each(clear) - - local foo_bar_map_table = { - lhs='foo', - script=0, - silent=0, - rhs='bar', - expr=0, - sid=0, - buffer=0, - nowait=0, - mode='n', - noremap=1, - lnum=0, - } - - it('returns a dictionary', function() - nvim('command', 'nnoremap foo bar') - eq('bar', funcs.maparg('foo')) - eq(foo_bar_map_table, funcs.maparg('foo', 'n', false, true)) - end) - - it('returns 1 for silent when is used', function() - nvim('command', 'nnoremap foo bar') - eq(1, funcs.maparg('foo', 'n', false, true)['silent']) - - nvim('command', 'nnoremap baz bat') - eq(0, funcs.maparg('baz', 'n', false, true)['silent']) - end) - - it('returns an empty string when no map is present', function() - eq('', funcs.maparg('not a mapping')) - end) - - it('returns an empty dictionary when no map is present and dict is requested', function() - eq({}, funcs.maparg('not a mapping', 'n', false, true)) - end) - - it('returns the same value for noremap and