diff options
author | b-r-o-c-k <brockmammen@gmail.com> | 2018-04-14 14:17:51 -0500 |
---|---|---|
committer | b-r-o-c-k <brockmammen@gmail.com> | 2018-04-14 14:17:51 -0500 |
commit | ad999eaa775d7d4b0cacedb30c6ea3a0ee699a6f (patch) | |
tree | 92de2079e80f5f289dd87a54af123cb7d90c3058 /test/functional | |
parent | 78bc52ea5397c092d01cd08296fe1dc85d998329 (diff) | |
parent | ef4feab0e75be19c5f41d70a001db980b72090f5 (diff) | |
download | rneovim-ad999eaa775d7d4b0cacedb30c6ea3a0ee699a6f.tar.gz rneovim-ad999eaa775d7d4b0cacedb30c6ea3a0ee699a6f.tar.bz2 rneovim-ad999eaa775d7d4b0cacedb30c6ea3a0ee699a6f.zip |
Merge branch 'master' into s-dash-stdin
Diffstat (limited to 'test/functional')
113 files changed, 5343 insertions, 1346 deletions
diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index 2297a0760f..fed53a3dfd 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -99,5 +99,14 @@ describe('highlight api',function() eq(false, err) eq('Invalid highlight name: ', string.match(emsg, 'Invalid.*')) + + -- Test "standout" attribute. #8054 + eq({ underline = true, }, + meths.get_hl_by_name('cursorline', 0)); + command('hi CursorLine cterm=standout,underline term=standout,underline gui=standout,underline') + command('set cursorline') + eq({ underline = true, standout = true, }, + meths.get_hl_by_name('cursorline', 0)); + end) end) diff --git a/test/functional/api/proc_spec.lua b/test/functional/api/proc_spec.lua new file mode 100644 index 0000000000..d99c26b6c2 --- /dev/null +++ b/test/functional/api/proc_spec.lua @@ -0,0 +1,81 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local eq = helpers.eq +local funcs = helpers.funcs +local iswin = helpers.iswin +local nvim_argv = helpers.nvim_argv +local ok = helpers.ok +local request = helpers.request +local retry = helpers.retry +local NIL = helpers.NIL + +describe('api', function() + before_each(clear) + + describe('nvim_get_proc_children', function() + it('returns child process ids', function() + local this_pid = funcs.getpid() + + local job1 = funcs.jobstart(nvim_argv) + retry(nil, nil, function() + eq(1, #request('nvim_get_proc_children', this_pid)) + end) + + local job2 = funcs.jobstart(nvim_argv) + retry(nil, nil, function() + eq(2, #request('nvim_get_proc_children', this_pid)) + end) + + funcs.jobstop(job1) + retry(nil, nil, function() + eq(1, #request('nvim_get_proc_children', this_pid)) + end) + + funcs.jobstop(job2) + retry(nil, nil, function() + eq(0, #request('nvim_get_proc_children', this_pid)) + end) + end) + + it('validates input', function() + local status, rv = pcall(request, "nvim_get_proc_children", -1) + eq(false, status) + eq("Invalid pid: -1", string.match(rv, "Invalid.*")) + + status, rv = pcall(request, "nvim_get_proc_children", 0) + eq(false, status) + eq("Invalid pid: 0", string.match(rv, "Invalid.*")) + + -- Assume PID 99999 does not exist. + status, rv = pcall(request, "nvim_get_proc_children", 99999) + eq(true, status) + eq({}, rv) + end) + end) + + describe('nvim_get_proc', function() + it('returns process info', function() + local pid = funcs.getpid() + local pinfo = request('nvim_get_proc', pid) + eq((iswin() and 'nvim.exe' or 'nvim'), pinfo.name) + ok(pinfo.pid == pid) + ok(type(pinfo.ppid) == 'number' and pinfo.ppid ~= pid) + end) + + it('validates input', function() + local status, rv = pcall(request, "nvim_get_proc", -1) + eq(false, status) + eq("Invalid pid: -1", string.match(rv, "Invalid.*")) + + status, rv = pcall(request, "nvim_get_proc", 0) + eq(false, status) + eq("Invalid pid: 0", string.match(rv, "Invalid.*")) + + -- Assume PID 99999 does not exist. + status, rv = pcall(request, "nvim_get_proc", 99999) + eq(true, status) + eq(NIL, rv) + end) + end) +end) diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua index 9d7cfb9b78..1d64ae7103 100644 --- a/test/functional/api/server_notifications_spec.lua +++ b/test/functional/api/server_notifications_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) -local eq, clear, eval, command, nvim, next_message = +local eq, clear, eval, command, nvim, next_msg = helpers.eq, helpers.clear, helpers.eval, helpers.command, helpers.nvim, - helpers.next_message + helpers.next_msg local meths = helpers.meths describe('notify', function() @@ -15,10 +15,10 @@ describe('notify', function() describe('passing a valid channel id', function() it('sends the notification/args to the corresponding channel', function() eval('rpcnotify('..channel..', "test-event", 1, 2, 3)') - eq({'notification', 'test-event', {1, 2, 3}}, next_message()) + eq({'notification', 'test-event', {1, 2, 3}}, next_msg()) command('au FileType lua call rpcnotify('..channel..', "lua!")') command('set filetype=lua') - eq({'notification', 'lua!', {}}, next_message()) + eq({'notification', 'lua!', {}}, next_msg()) end) end) @@ -28,13 +28,13 @@ describe('notify', function() eval('rpcnotify(0, "event1", 1, 2, 3)') eval('rpcnotify(0, "event2", 4, 5, 6)') eval('rpcnotify(0, "event2", 7, 8, 9)') - eq({'notification', 'event2', {4, 5, 6}}, next_message()) - eq({'notification', 'event2', {7, 8, 9}}, next_message()) + eq({'notification', 'event2', {4, 5, 6}}, next_msg()) + eq({'notification', 'event2', {7, 8, 9}}, next_msg()) nvim('unsubscribe', 'event2') nvim('subscribe', 'event1') eval('rpcnotify(0, "event2", 10, 11, 12)') eval('rpcnotify(0, "event1", 13, 14, 15)') - eq({'notification', 'event1', {13, 14, 15}}, next_message()) + eq({'notification', 'event1', {13, 14, 15}}, next_msg()) end) it('does not crash for deeply nested variable', function() @@ -42,7 +42,7 @@ describe('notify', function() local nest_level = 1000 meths.command(('call map(range(%u), "extend(g:, {\'l\': [g:l]})")'):format(nest_level - 1)) eval('rpcnotify('..channel..', "event", g:l)') - local msg = next_message() + local msg = next_msg() eq('notification', msg[1]) eq('event', msg[2]) local act_ret = msg[3] diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index 37ac532d18..18229b54ff 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -6,7 +6,7 @@ local Paths = require('test.config.paths') local clear, nvim, eval = helpers.clear, helpers.nvim, helpers.eval local eq, neq, run, stop = helpers.eq, helpers.neq, helpers.run, helpers.stop local nvim_prog, command, funcs = helpers.nvim_prog, helpers.command, helpers.funcs -local source, next_message = helpers.source, helpers.next_message +local source, next_msg = helpers.source, helpers.next_msg local ok = helpers.ok local meths = helpers.meths local spawn, nvim_argv = helpers.spawn, helpers.nvim_argv @@ -258,12 +258,12 @@ describe('server -> client', function() it('rpc and text stderr can be combined', function() eq("ok",funcs.rpcrequest(jobid, "poll")) funcs.rpcnotify(jobid, "ping") - eq({'notification', 'pong', {}}, next_message()) + eq({'notification', 'pong', {}}, next_msg()) eq("done!",funcs.rpcrequest(jobid, "write_stderr", "fluff\n")) - eq({'notification', 'stderr', {0, {'fluff', ''}}}, next_message()) + eq({'notification', 'stderr', {0, {'fluff', ''}}}, next_msg()) funcs.rpcrequest(jobid, "exit") - eq({'notification', 'stderr', {0, {''}}}, next_message()) - eq({'notification', 'exit', {0, 0}}, next_message()) + eq({'notification', 'stderr', {0, {''}}}, next_msg()) + eq({'notification', 'exit', {0, 0}}, next_msg()) end) end) diff --git a/test/functional/api/ui_spec.lua b/test/functional/api/ui_spec.lua new file mode 100644 index 0000000000..b028a50b02 --- /dev/null +++ b/test/functional/api/ui_spec.lua @@ -0,0 +1,37 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval +local expect_err = helpers.expect_err +local meths = helpers.meths +local request = helpers.request + +describe('nvim_ui_attach()', function() + before_each(function() + clear() + end) + it('handles very large width/height #2180', function() + local screen = Screen.new(999, 999) + screen:attach() + eq(999, eval('&lines')) + eq(999, eval('&columns')) + end) + it('invalid option returns error', function() + expect_err('No such UI option: foo', + meths.ui_attach, 80, 24, { foo={'foo'} }) + end) + it('validates channel arg', function() + expect_err('UI not attached to channel: 1', + request, 'nvim_ui_try_resize', 40, 10) + expect_err('UI not attached to channel: 1', + request, 'nvim_ui_set_option', 'rgb', true) + expect_err('UI not attached to channel: 1', + request, 'nvim_ui_detach') + + local screen = Screen.new() + screen:attach({rgb=false}) + expect_err('UI already attached to channel: 1', + request, 'nvim_ui_attach', 40, 10, { rgb=false }) + end) +end) diff --git a/test/functional/api/version_spec.lua b/test/functional/api/version_spec.lua index d23f058f69..7bf54c0d1e 100644 --- a/test/functional/api/version_spec.lua +++ b/test/functional/api/version_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local mpack = require('mpack') local clear, funcs, eq = helpers.clear, helpers.funcs, helpers.eq +local call = helpers.call local function read_mpack_file(fname) local fd = io.open(fname, 'rb') @@ -18,7 +19,7 @@ describe("api_info()['version']", function() before_each(clear) it("returns API level", function() - local version = helpers.call('api_info')['version'] + local version = call('api_info')['version'] local current = version['api_level'] local compat = version['api_compatible'] eq("number", type(current)) @@ -27,7 +28,7 @@ describe("api_info()['version']", function() end) it("returns Nvim version", function() - local version = helpers.call('api_info')['version'] + local version = call('api_info')['version'] local major = version['major'] local minor = version['minor'] local patch = version['patch'] @@ -147,3 +148,14 @@ describe("api functions", function() end) end) + +describe("ui_options in metadata", function() + it('are correct', function() + -- TODO(bfredl) once a release freezes this into metadata, + -- instead check that all old options are present + local api = helpers.call('api_info') + local options = api.ui_options + eq({'rgb', 'ext_cmdline', 'ext_popupmenu', + 'ext_tabline', 'ext_wildmenu'}, options) + end) +end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index b849304d45..718294d941 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1,5 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local global_helpers = require('test.helpers') + local NIL = helpers.NIL local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed @@ -9,6 +11,11 @@ local funcs = helpers.funcs local request = helpers.request local meth_pcall = helpers.meth_pcall local command = helpers.command +local iswin = helpers.iswin + +local intchar2lua = global_helpers.intchar2lua +local format_string = global_helpers.format_string +local mergedicts_copy = global_helpers.mergedicts_copy describe('api', function() before_each(clear) @@ -31,7 +38,7 @@ describe('api', function() os.remove(fname) end) - it("VimL error: fails (VimL error), does NOT update v:errmsg", function() + it("parse error: fails (specific error), does NOT update v:errmsg", function() -- Most API methods return generic errors (or no error) if a VimL -- expression fails; nvim_command returns the VimL error details. local status, rv = pcall(nvim, "command", "bogus_command") @@ -39,6 +46,85 @@ describe('api', function() eq("E492:", string.match(rv, "E%d*:")) -- VimL error was returned. eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated. end) + + it("runtime error: fails (specific error)", function() + local status, rv = pcall(nvim, "command_output", "buffer 23487") + eq(false, status) -- nvim_command() failed. + eq("E86: Buffer 23487 does not exist", string.match(rv, "E%d*:.*")) + eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated. + end) + end) + + describe('nvim_command_output', function() + it('does not induce hit-enter prompt', function() + -- Induce a hit-enter prompt use nvim_input (non-blocking). + nvim('command', 'set cmdheight=1') + nvim('input', [[:echo "hi\nhi2"<CR>]]) + + -- Verify hit-enter prompt. + eq({mode='r', blocking=true}, nvim("get_mode")) + nvim('input', [[<C-c>]]) + + -- Verify NO hit-enter prompt. + nvim('command_output', [[echo "hi\nhi2"]]) + eq({mode='n', blocking=false}, nvim("get_mode")) + end) + + it('captures command output', function() + eq('this is\nspinal tap', + nvim('command_output', [[echo "this is\nspinal tap"]])) + eq('no line ending!', + nvim('command_output', [[echon "no line ending!"]])) + end) + + it('captures empty command output', function() + eq('', nvim('command_output', 'echo')) + end) + + it('captures single-char command output', function() + eq('x', nvim('command_output', 'echo "x"')) + end) + + it('captures multiple commands', function() + eq('foo\n 1 %a "[No Name]" line 1', + nvim('command_output', 'echo "foo" | ls')) + end) + + it('captures nested execute()', function() + eq('\nnested1\nnested2\n 1 %a "[No Name]" line 1', + nvim('command_output', + [[echo execute('echo "nested1\nnested2"') | ls]])) + end) + + it('captures nested nvim_command_output()', function() + eq('nested1\nnested2\n 1 %a "[No Name]" line 1', + nvim('command_output', + [[echo nvim_command_output('echo "nested1\nnested2"') | ls]])) + end) + + it('returns shell |:!| output', function() + local win_lf = iswin() and '\r' or '' + eq(':!echo foo\r\n\nfoo'..win_lf..'\n', nvim('command_output', [[!echo foo]])) + end) + + it("parse error: fails (specific error), does NOT update v:errmsg", function() + local status, rv = pcall(nvim, "command_output", "bogus commannnd") + eq(false, status) -- nvim_command_output() failed. + eq("E492: Not an editor command: bogus commannnd", + string.match(rv, "E%d*:.*")) + eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated. + -- Verify NO hit-enter prompt. + eq({mode='n', blocking=false}, nvim("get_mode")) + end) + + it("runtime error: fails (specific error)", function() + local status, rv = pcall(nvim, "command_output", "buffer 42") + eq(false, status) -- nvim_command_output() failed. + eq("E86: Buffer 42 does not exist", string.match(rv, "E%d*:.*")) + eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated. + -- Verify NO hit-enter prompt. + eq({mode='n', blocking=false}, nvim("get_mode")) + end) end) describe('nvim_eval', function() @@ -495,7 +581,8 @@ describe('api', function() screen:set_default_attr_ids({ [0] = {bold=true, foreground=Screen.colors.Blue}, [1] = {foreground = Screen.colors.White, background = Screen.colors.Red}, - [2] = {bold = true, foreground = Screen.colors.SeaGreen} + [2] = {bold = true, foreground = Screen.colors.SeaGreen}, + [3] = {bold = true, reverse = true}, }) end) @@ -516,11 +603,11 @@ describe('api', function() it('shows return prompt when more than &cmdheight lines', function() nvim_async('err_write', 'something happened\nvery bad\n') screen:expect([[ + | {0:~ }| {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| + {3: }| {1:something happened} | {1:very bad} | {2:Press ENTER or type command to continue}^ | @@ -530,9 +617,9 @@ describe('api', function() it('shows return prompt after all lines are shown', function() nvim_async('err_write', 'FAILURE\nERROR\nEXCEPTION\nTRACEBACK\n') screen:expect([[ + | {0:~ }| - {0:~ }| - {0:~ }| + {3: }| {1:FAILURE} | {1:ERROR} | {1:EXCEPTION} | @@ -560,11 +647,11 @@ describe('api', function() -- shows up to &cmdheight lines nvim_async('err_write', 'more fail\ntoo fail\n') screen:expect([[ + | {0:~ }| {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| + {3: }| {1:more fail} | {1:too fail} | {2:Press ENTER or type command to continue}^ | @@ -661,7 +748,7 @@ describe('api', function() end) end) - describe('list_runtime_paths', function() + describe('nvim_list_runtime_paths', function() it('returns nothing with empty &runtimepath', function() meths.set_option('runtimepath', '') eq({}, meths.list_runtime_paths()) @@ -710,4 +797,247 @@ describe('api', function() ok(err:match(': Wrong type for argument 1, expecting String') ~= nil) end) + describe('nvim_parse_expression', function() + before_each(function() + meths.set_option('isident', '') + end) + local function simplify_east_api_node(line, east_api_node) + if east_api_node == NIL then + return nil + end + if east_api_node.children then + for k, v in pairs(east_api_node.children) do + east_api_node.children[k] = simplify_east_api_node(line, v) + end + end + local typ = east_api_node.type + if typ == 'Register' then + typ = typ .. ('(name=%s)'):format( + tostring(intchar2lua(east_api_node.name))) + east_api_node.name = nil + elseif typ == 'PlainIdentifier' then + typ = typ .. ('(scope=%s,ident=%s)'):format( + tostring(intchar2lua(east_api_node.scope)), east_api_node.ident) + east_api_node.scope = nil + east_api_node.ident = nil + elseif typ == 'PlainKey' then + typ = typ .. ('(key=%s)'):format(east_api_node.ident) + east_api_node.ident = nil + elseif typ == 'Comparison' then + typ = typ .. ('(type=%s,inv=%u,ccs=%s)'):format( + east_api_node.cmp_type, east_api_node.invert and 1 or 0, + east_api_node.ccs_strategy) + east_api_node.ccs_strategy = nil + east_api_node.cmp_type = nil + east_api_node.invert = nil + elseif typ == 'Integer' then + typ = typ .. ('(val=%u)'):format(east_api_node.ivalue) + east_api_node.ivalue = nil + elseif typ == 'Float' then + typ = typ .. format_string('(val=%e)', east_api_node.fvalue) + east_api_node.fvalue = nil + elseif typ == 'SingleQuotedString' or typ == 'DoubleQuotedString' then + typ = format_string('%s(val=%q)', typ, east_api_node.svalue) + east_api_node.svalue = nil + elseif typ == 'Option' then + typ = ('%s(scope=%s,ident=%s)'):format( + typ, + tostring(intchar2lua(east_api_node.scope)), + east_api_node.ident) + east_api_node.ident = nil + east_api_node.scope = nil + elseif typ == 'Environment' then + typ = ('%s(ident=%s)'):format(typ, east_api_node.ident) + east_api_node.ident = nil + elseif typ == 'Assignment' then + local aug = east_api_node.augmentation + if aug == '' then aug = 'Plain' end + typ = ('%s(%s)'):format(typ, aug) + east_api_node.augmentation = nil + end + typ = ('%s:%u:%u:%s'):format( + typ, east_api_node.start[1], east_api_node.start[2], + line:sub(east_api_node.start[2] + 1, + east_api_node.start[2] + 1 + east_api_node.len - 1)) + assert(east_api_node.start[2] + east_api_node.len - 1 <= #line) + for k, _ in pairs(east_api_node.start) do + assert(({true, true})[k]) + end + east_api_node.start = nil + east_api_node.type = nil + east_api_node.len = nil + local can_simplify = true + for _, _ in pairs(east_api_node) do + if can_simplify then can_simplify = false end + end + if can_simplify then + return typ + else + east_api_node[1] = typ + return east_api_node + end + end + local function simplify_east_api(line, east_api) + if east_api.error then + east_api.err = east_api.error + east_api.error = nil + east_api.err.msg = east_api.err.message + east_api.err.message = nil + end + if east_api.ast then + east_api.ast = {simplify_east_api_node(line, east_api.ast)} + if #east_api.ast == 0 then + east_api.ast = nil + end + end + if east_api.len == #line then + east_api.len = nil + end + return east_api + end + local function simplify_east_hl(line, east_hl) + for i, v in ipairs(east_hl) do + east_hl[i] = ('%s:%u:%u:%s'):format( + v[4], + v[1], + v[2], + line:sub(v[2] + 1, v[3])) + end + return east_hl + end + local FLAGS_TO_STR = { + [0] = "", + [1] = "m", + [2] = "E", + [3] = "mE", + [4] = "l", + [5] = "lm", + [6] = "lE", + [7] = "lmE", + } + local function _check_parsing(opts, str, exp_ast, exp_highlighting_fs, + nz_flags_exps) + if type(str) ~= 'string' then + return + end + local zflags = opts.flags[1] + nz_flags_exps = nz_flags_exps or {} + for _, flags in ipairs(opts.flags) do + local err, msg = pcall(function() + local east_api = meths.parse_expression(str, FLAGS_TO_STR[flags], true) + local east_hl = east_api.highlight + east_api.highlight = nil + local ast = simplify_east_api(str, east_api) + local hls = simplify_east_hl(str, east_hl) + local exps = { + ast = exp_ast, + hl_fs = exp_highlighting_fs, + } + local add_exps = nz_flags_exps[flags] + if not add_exps and flags == 3 + zflags then + add_exps = nz_flags_exps[1 + zflags] or nz_flags_exps[2 + zflags] + end + if add_exps then + if add_exps.ast then + exps.ast = mergedicts_copy(exps.ast, add_exps.ast) + end + if add_exps.hl_fs then + exps.hl_fs = mergedicts_copy(exps.hl_fs, add_exps.hl_fs) + end + end + eq(exps.ast, ast) + if exp_highlighting_fs then + local exp_highlighting = {} + local next_col = 0 + for i, h in ipairs(exps.hl_fs) do + exp_highlighting[i], next_col = h(next_col) + end + eq(exp_highlighting, hls) + end + end) + if not err then + if type(msg) == 'table' then + local merr, new_msg = pcall( + format_string, 'table error:\n%s\n\n(%r)', msg.message, msg) + if merr then + msg = new_msg + else + msg = format_string('table error without .message:\n(%r)', + msg) + end + elseif type(msg) ~= 'string' then + msg = format_string('non-string non-table error:\n%r', msg) + end + error(format_string('Error while processing test (%r, %s):\n%s', + str, FLAGS_TO_STR[flags], msg)) + end + end + end + local function hl(group, str, shift) + return function(next_col) + local col = next_col + (shift or 0) + return (('%s:%u:%u:%s'):format( + 'Nvim' .. group, + 0, + col, + str)), (col + #str) + end + end + local function fmtn(typ, args, rest) + if (typ == 'UnknownFigure' + or typ == 'DictLiteral' + or typ == 'CurlyBracesIdentifier' + or typ == 'Lambda') then + return ('%s%s'):format(typ, rest) + elseif typ == 'DoubleQuotedString' or typ == 'SingleQuotedString' then + if args:sub(-4) == 'NULL' then + args = args:sub(1, -5) .. '""' + end + return ('%s(%s)%s'):format(typ, args, rest) + end + end + assert:set_parameter('TableFormatLevel', 1000000) + require('test.unit.viml.expressions.parser_tests')( + it, _check_parsing, hl, fmtn) + end) + + describe('nvim_list_uis', function() + it('returns empty if --headless', function() + -- --embed implies --headless. + eq({}, nvim("list_uis")) + end) + it('returns attached UIs', function() + local screen = Screen.new(20, 4) + screen:attach() + local expected = { + { + ext_cmdline = false, + ext_popupmenu = false, + ext_tabline = false, + ext_wildmenu = false, + height = 4, + rgb = true, + width = 20, + } + } + eq(expected, nvim("list_uis")) + + screen:detach() + screen = Screen.new(44, 99) + screen:attach({ rgb = false }) + expected = { + { + ext_cmdline = false, + ext_popupmenu = false, + ext_tabline = false, + ext_wildmenu = false, + height = 99, + rgb = false, + width = 44, + } + } + eq(expected, nvim("list_uis")) + end) + end) + end) diff --git a/test/functional/autocmd/bufenter_spec.lua b/test/functional/autocmd/bufenter_spec.lua index fef9838050..e14ddb3316 100644 --- a/test/functional/autocmd/bufenter_spec.lua +++ b/test/functional/autocmd/bufenter_spec.lua @@ -31,4 +31,17 @@ describe('autocmd BufEnter', function() eq(1, eval("exists('g:dir_bufenter')")) -- Did BufEnter for the directory. eq(2, eval("bufnr('%')")) -- Switched to the dir buffer. end) + + it('triggered by ":split normal|:help|:bw"', function() + command("split normal") + command("wincmd j") + command("helptags runtime/doc") + command("help") + command("wincmd L") + command("autocmd BufEnter normal let g:bufentered = 1") + command("bw") + eq(1, eval('bufnr("%")')) -- The cursor is back to the bottom window + eq(0, eval("exists('g:bufentered')")) -- The autocmd hasn't been triggered + end) + end) diff --git a/test/functional/autocmd/cmdline_spec.lua b/test/functional/autocmd/cmdline_spec.lua index 8d56687f7d..3f0504d02f 100644 --- a/test/functional/autocmd/cmdline_spec.lua +++ b/test/functional/autocmd/cmdline_spec.lua @@ -5,7 +5,7 @@ local clear = helpers.clear local command = helpers.command local eq = helpers.eq local expect = helpers.expect -local next_msg = helpers.next_message +local next_msg = helpers.next_msg local feed = helpers.feed local meths = helpers.meths @@ -59,24 +59,25 @@ describe('cmdline autocommands', function() [1] = {bold = true, foreground = Screen.colors.Blue1}, [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [4] = {bold = true, reverse = true}, }) command("autocmd CmdlineEnter * echoerr 'FAIL'") command("autocmd CmdlineLeave * echoerr 'very error'") feed(':') screen:expect([[ + | {1:~ }| {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| + {4: }| : | {2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} | :^ | ]]) feed("put ='lorem ipsum'<cr>") screen:expect([[ - {1:~ }| - {1:~ }| + | + {4: }| : | {2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} | :put ='lorem ipsum' | diff --git a/test/functional/autocmd/dirchanged_spec.lua b/test/functional/autocmd/dirchanged_spec.lua index 63cf0bc410..7979e91a4c 100644 --- a/test/functional/autocmd/dirchanged_spec.lua +++ b/test/functional/autocmd/dirchanged_spec.lua @@ -154,4 +154,11 @@ describe('autocmd DirChanged', function() eq('Failed to change directory', string.match(err, ': (.*)')) eq({cwd=dirs[2], scope='global'}, eval('g:ev')) end) + + it('works when local to buffer', function() + command('let g:triggered = 0') + command('autocmd DirChanged <buffer> let g:triggered = 1') + command('cd '..dirs[1]) + eq(1, eval('g:triggered')) + end) end) diff --git a/test/functional/autocmd/filetype_spec.lua b/test/functional/autocmd/filetype_spec.lua new file mode 100644 index 0000000000..e6fa7ab6bb --- /dev/null +++ b/test/functional/autocmd/filetype_spec.lua @@ -0,0 +1,16 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eval = helpers.eval +local clear = helpers.clear +local command = helpers.command + +describe('autocmd FileType', function() + before_each(clear) + + it("is triggered by :help only once", function() + command("let g:foo = 0") + command("autocmd FileType help let g:foo = g:foo + 1") + command("help help") + assert.same(1, eval('g:foo')) + end) +end) diff --git a/test/functional/autocmd/tabclose_spec.lua b/test/functional/autocmd/tabclose_spec.lua index 1431c69589..b7c33dc3d8 100644 --- a/test/functional/autocmd/tabclose_spec.lua +++ b/test/functional/autocmd/tabclose_spec.lua @@ -2,32 +2,67 @@ local helpers = require('test.functional.helpers')(after_each) local clear, nvim, eq = helpers.clear, helpers.nvim, helpers.eq describe('TabClosed', function() - describe('au TabClosed', function() - describe('with * as <afile>', function() - it('matches when closing any tab', function() - clear() - nvim('command', 'au! TabClosed * echom "tabclosed:".expand("<afile>").":".expand("<amatch>").":".tabpagenr()') - repeat - nvim('command', 'tabnew') - until nvim('eval', 'tabpagenr()') == 6 -- current tab is now 6 - eq("\ntabclosed:6:6:5", nvim('command_output', 'tabclose')) -- close last 6, current tab is now 5 - eq("\ntabclosed:5:5:4", nvim('command_output', 'close')) -- close last window on tab, closes tab - eq("\ntabclosed:2:2:3", nvim('command_output', '2tabclose')) -- close tab 2, current tab is now 3 - eq("\ntabclosed:1:1:2\ntabclosed:1:1:1", nvim('command_output', 'tabonly')) -- close tabs 1 and 2 - end) - end) - describe('with NR as <afile>', function() - it('matches when closing a tab whose index is NR', function() - nvim('command', 'au! TabClosed 2 echom "tabclosed:match"') - repeat - nvim('command', 'tabnew') - until nvim('eval', 'tabpagenr()') == 5 -- current tab is now 5 - -- sanity check, we shouldn't match on tabs with numbers other than 2 - eq("\ntabclosed:5:5:4", nvim('command_output', 'tabclose')) - -- close tab page 2, current tab is now 3 - eq("\ntabclosed:2:2:3\ntabclosed:match", nvim('command_output', '2tabclose')) - end) - end) + before_each(clear) + + describe('au TabClosed', function() + describe('with * as <afile>', function() + it('matches when closing any tab', function() + nvim('command', 'au! TabClosed * echom "tabclosed:".expand("<afile>").":".expand("<amatch>").":".tabpagenr()') + repeat + nvim('command', 'tabnew') + until nvim('eval', 'tabpagenr()') == 6 -- current tab is now 6 + eq("tabclosed:6:6:5", nvim('command_output', 'tabclose')) -- close last 6, current tab is now 5 + eq("tabclosed:5:5:4", nvim('command_output', 'close')) -- close last window on tab, closes tab + eq("tabclosed:2:2:3", nvim('command_output', '2tabclose')) -- close tab 2, current tab is now 3 + eq("tabclosed:1:1:2\ntabclosed:1:1:1", nvim('command_output', 'tabonly')) -- close tabs 1 and 2 + end) + + it('is triggered when closing a window via bdelete from another tab', function() + nvim('command', 'au! TabClosed * echom "tabclosed:".expand("<afile>").":".expand("<amatch>").":".tabpagenr()') + nvim('command', '1tabedit Xtestfile') + nvim('command', '1tabedit Xtestfile') + nvim('command', 'normal! 1gt') + eq({1, 3}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) + eq("tabclosed:2:2:1\ntabclosed:2:2:1", nvim('command_output', 'bdelete Xtestfile')) + eq({1, 1}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) + end) + + it('is triggered when closing a window via bdelete from current tab', function() + nvim('command', 'au! TabClosed * echom "tabclosed:".expand("<afile>").":".expand("<amatch>").":".tabpagenr()') + nvim('command', 'file Xtestfile1') + nvim('command', '1tabedit Xtestfile2') + nvim('command', '1tabedit Xtestfile2') + + -- Only one tab is closed, and the alternate file is used for the other. + eq({2, 3}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) + eq("tabclosed:2:2:2", nvim('command_output', 'bdelete Xtestfile2')) + eq('Xtestfile1', nvim('eval', 'bufname("")')) + end) + end) + + describe('with NR as <afile>', function() + it('matches when closing a tab whose index is NR', function() + nvim('command', 'au! TabClosed * echom "tabclosed:".expand("<afile>").":".expand("<amatch>").":".tabpagenr()') + nvim('command', 'au! TabClosed 2 echom "tabclosed:match"') + repeat + nvim('command', 'tabnew') + until nvim('eval', 'tabpagenr()') == 7 -- current tab is now 7 + -- sanity check, we shouldn't match on tabs with numbers other than 2 + eq("tabclosed:7:7:6", nvim('command_output', 'tabclose')) + -- close tab page 2, current tab is now 5 + eq("tabclosed:2:2:5\ntabclosed:match", nvim('command_output', '2tabclose')) + end) + end) + + describe('with close', function() + it('is triggered', function() + nvim('command', 'au! TabClosed * echom "tabclosed:".expand("<afile>").":".expand("<amatch>").":".tabpagenr()') + nvim('command', 'tabedit Xtestfile') + eq({2, 2}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) + eq("tabclosed:2:2:1", nvim('command_output', 'close')) + eq({1, 1}, nvim('eval', '[tabpagenr(), tabpagenr("$")]')) + end) end) + end) end) diff --git a/test/functional/autocmd/tabnewentered_spec.lua b/test/functional/autocmd/tabnewentered_spec.lua index bdbe677132..59cac07b34 100644 --- a/test/functional/autocmd/tabnewentered_spec.lua +++ b/test/functional/autocmd/tabnewentered_spec.lua @@ -7,14 +7,14 @@ describe('TabNewEntered', function() it('matches when entering any new tab', function() clear() nvim('command', 'au! TabNewEntered * echom "tabnewentered:".tabpagenr().":".bufnr("")') - eq("\ntabnewentered:2:2", nvim('command_output', 'tabnew')) - eq("\n\"test.x2\" [New File]\ntabnewentered:3:3", nvim('command_output', 'tabnew test.x2')) + eq("tabnewentered:2:2", nvim('command_output', 'tabnew')) + eq("tabnewentered:3:3", nvim('command_output', 'tabnew test.x2')) end) end) describe('with FILE as <afile>', function() it('matches when opening a new tab for FILE', function() nvim('command', 'au! TabNewEntered Xtest-tabnewentered echom "tabnewentered:match"') - eq('\n"Xtest-tabnewentered" [New File]\ntabnewentered:4:4\ntabnewentered:match', + eq('tabnewentered:4:4\ntabnewentered:match', nvim('command_output', 'tabnew Xtest-tabnewentered')) end) end) @@ -24,7 +24,7 @@ describe('TabNewEntered', function() nvim('command', 'au! TabNewEntered * echom "entered"') nvim('command', 'tabnew test.x2') nvim('command', 'split') - eq('\nentered', nvim('command_output', 'execute "normal \\<C-W>T"')) + eq('entered', nvim('command_output', 'execute "normal \\<C-W>T"')) end) end) end) diff --git a/test/functional/autocmd/termclose_spec.lua b/test/functional/autocmd/termclose_spec.lua index c6c30494dd..0804579a4f 100644 --- a/test/functional/autocmd/termclose_spec.lua +++ b/test/functional/autocmd/termclose_spec.lua @@ -1,11 +1,13 @@ +local luv = require('luv') local helpers = require('test.functional.helpers')(after_each) local clear, command, nvim, nvim_dir = helpers.clear, helpers.command, helpers.nvim, helpers.nvim_dir local eval, eq, retry = helpers.eval, helpers.eq, helpers.retry +local ok = helpers.ok +local iswin = helpers.iswin -if helpers.pending_win32(pending) then return end describe('TermClose event', function() before_each(function() @@ -22,7 +24,7 @@ describe('TermClose event', function() end) it('triggers when long-running terminal job gets stopped', function() - nvim('set_option', 'shell', 'sh') + nvim('set_option', 'shell', iswin() and 'cmd.exe' or 'sh') command('autocmd TermClose * let g:test_termclose = 23') command('terminal') command('call jobstop(b:terminal_job_id)') @@ -30,6 +32,7 @@ describe('TermClose event', function() end) it('kills job trapping SIGTERM', function() + if helpers.pending_win32(pending) then return end nvim('set_option', 'shell', 'sh') nvim('set_option', 'shellcmdflag', '-c') command([[ let g:test_job = jobstart('trap "" TERM && echo 1 && sleep 60', { ]] @@ -37,14 +40,19 @@ describe('TermClose event', function() .. [[ 'on_exit': {-> execute('let g:test_job_exited = 1')}}) ]]) retry(nil, nil, function() eq(1, eval('get(g:, "test_job_started", 0)')) end) - local start = os.time() + luv.update_time() + local start = luv.now() command('call jobstop(g:test_job)') retry(nil, nil, function() eq(1, eval('get(g:, "test_job_exited", 0)')) end) - local duration = os.time() - start - eq(2, duration) + luv.update_time() + local duration = luv.now() - start + -- Nvim begins SIGTERM after KILL_TIMEOUT_MS. + ok(duration >= 2000) + ok(duration <= 4000) -- Epsilon for slow CI end) it('kills pty job trapping SIGHUP and SIGTERM', function() + if helpers.pending_win32(pending) then return end nvim('set_option', 'shell', 'sh') nvim('set_option', 'shellcmdflag', '-c') command([[ let g:test_job = jobstart('trap "" HUP TERM && echo 1 && sleep 60', { ]] @@ -53,13 +61,15 @@ describe('TermClose event', function() .. [[ 'on_exit': {-> execute('let g:test_job_exited = 1')}}) ]]) retry(nil, nil, function() eq(1, eval('get(g:, "test_job_started", 0)')) end) - local start = os.time() + luv.update_time() + local start = luv.now() command('call jobstop(g:test_job)') retry(nil, nil, function() eq(1, eval('get(g:, "test_job_exited", 0)')) end) - local duration = os.time() - start - -- nvim starts sending kill after 2*KILL_TIMEOUT_MS - helpers.ok(4 <= duration) - helpers.ok(duration <= 7) -- <= 4 + delta because of slow CI + luv.update_time() + local duration = luv.now() - start + -- Nvim begins SIGKILL after (2 * KILL_TIMEOUT_MS). + ok(duration >= 4000) + ok(duration <= 7000) -- Epsilon for slow CI end) it('reports the correct <abuf>', function() diff --git a/test/functional/clipboard/clipboard_provider_spec.lua b/test/functional/clipboard/clipboard_provider_spec.lua index a3ea3b568f..a40c080a6d 100644 --- a/test/functional/clipboard/clipboard_provider_spec.lua +++ b/test/functional/clipboard/clipboard_provider_spec.lua @@ -83,7 +83,14 @@ local function basic_register_test(noblock) end describe('clipboard', function() - before_each(clear) + local screen + + before_each(function() + clear() + screen = Screen.new(72, 4) + screen:attach() + command("set display-=msgsep") + end) it('unnamed register works without provider', function() eq('"', eval('v:register')) @@ -92,8 +99,6 @@ describe('clipboard', function() it('`:redir @+>` with invalid g:clipboard shows exactly one error #7184', function() - local screen = Screen.new(72, 4) - screen:attach() command("let g:clipboard = 'bogus'") feed_command('redir @+> | :silent echo system("cat CONTRIBUTING.md") | redir END') screen:expect([[ @@ -106,8 +111,6 @@ describe('clipboard', function() it('`:redir @+>|bogus_cmd|redir END` + invalid g:clipboard must not recurse #7184', function() - local screen = Screen.new(72, 4) - screen:attach() command("let g:clipboard = 'bogus'") feed_command('redir @+> | bogus_cmd | redir END') screen:expect([[ @@ -123,8 +126,6 @@ describe('clipboard', function() eq('', eval('provider#clipboard#Executable()')) eq('clipboard: invalid g:clipboard', eval('provider#clipboard#Error()')) - local screen = Screen.new(72, 4) - screen:attach() command("let g:clipboard = 'bogus'") -- Explicit clipboard attempt, should show a hint message. feed_command('let @+="foo"') @@ -493,10 +494,10 @@ describe('clipboard', function() feed_command("let g:test_clip['+'] = ['such', 'plus', 'stuff']") feed_command("registers") screen:expect([[ - ~ | - ~ | - ~ | - ~ | + | + {0:~ }| + {0:~ }| + {4: }| :registers | {1:--- Registers ---} | "* some{2:^J}star data{2:^J} | @@ -504,10 +505,11 @@ describe('clipboard', function() ": let g:test_clip['+'] = ['such', 'plus', 'stuff'] | {3:Press ENTER or type command to continue}^ | ]], { + [0] = {bold = true, foreground = Screen.colors.Blue}, [1] = {bold = true, foreground = Screen.colors.Fuchsia}, [2] = {foreground = Screen.colors.Blue}, - [3] = {bold = true, foreground = Screen.colors.SeaGreen}}, - {{bold = true, foreground = Screen.colors.Blue}}) + [3] = {bold = true, foreground = Screen.colors.SeaGreen}, + [4] = {bold = true, reverse = true}}) feed('<cr>') -- clear out of Press ENTER screen end) diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua index 765e3c5919..f79f208b69 100644 --- a/test/functional/core/channels_spec.lua +++ b/test/functional/core/channels_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, eq, eval, next_msg, ok, source = helpers.clear, helpers.eq, - helpers.eval, helpers.next_message, helpers.ok, helpers.source + helpers.eval, helpers.next_msg, helpers.ok, helpers.source local command, funcs, meths = helpers.command, helpers.funcs, helpers.meths local sleep = helpers.sleep local spawn, nvim_argv = helpers.spawn, helpers.nvim_argv @@ -246,6 +246,22 @@ describe('channels', function() eq({"notification", "exit", {id, 0, {'10 PRINT "NVIM"', '20 GOTO 10', ''}}}, next_msg()) + -- if dict is reused the new value is not stored, + -- but nvim also does not crash + command("let id = jobstart(['cat'], g:job_opts)") + id = eval("g:id") + + command([[call chansend(id, "cat text\n")]]) + sleep(10) + command("call chanclose(id, 'stdin')") + + -- old value was not overwritten + eq({"notification", "exit", {id, 0, {'10 PRINT "NVIM"', + '20 GOTO 10', ''}}}, next_msg()) + + -- and an error was thrown. + eq("E5210: dict key 'stdout' already set for buffered stream in channel "..id, eval('v:errmsg')) + -- reset dictionary source([[ let g:job_opts = { @@ -261,6 +277,5 @@ describe('channels', function() -- works correctly with no output eq({"notification", "exit", {id, 1, {''}}}, next_msg()) - end) end) diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua index 188a6a2c11..80c65e4544 100644 --- a/test/functional/core/exit_spec.lua +++ b/test/functional/core/exit_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local command = helpers.command +local feed_command = helpers.feed_command local eval = helpers.eval local eq = helpers.eq local run = helpers.run @@ -33,6 +34,18 @@ describe('v:exiting', function() end run(on_request, nil, on_setup) end) + it('is 0 on exit from ex-mode involving try-catch', function() + local function on_setup() + command('autocmd VimLeavePre * call rpcrequest('..cid..', "")') + command('autocmd VimLeave * call rpcrequest('..cid..', "")') + feed_command('call feedkey("Q")','try', 'call NoFunction()', 'catch', 'echo "bye"', 'endtry', 'quit') + end + local function on_request() + eq(0, eval('v:exiting')) + return '' + end + run(on_request, nil, on_setup) + end) end) describe(':cquit', function() diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index e957650c88..24bff423df 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -2,15 +2,21 @@ local helpers = require('test.functional.helpers')(after_each) local clear, eq, eval, exc_exec, feed_command, feed, insert, neq, next_msg, nvim, nvim_dir, ok, source, write_file, mkdir, rmdir = helpers.clear, helpers.eq, helpers.eval, helpers.exc_exec, helpers.feed_command, helpers.feed, - helpers.insert, helpers.neq, helpers.next_message, helpers.nvim, + helpers.insert, helpers.neq, helpers.next_msg, helpers.nvim, helpers.nvim_dir, helpers.ok, helpers.source, helpers.write_file, helpers.mkdir, helpers.rmdir local command = helpers.command +local funcs = helpers.funcs +local retry = helpers.retry +local meths = helpers.meths +local NIL = helpers.NIL local wait = helpers.wait local iswin = helpers.iswin local get_pathsep = helpers.get_pathsep +local pathroot = helpers.pathroot local nvim_set = helpers.nvim_set local expect_twostreams = helpers.expect_twostreams +local expect_msg_seq = helpers.expect_msg_seq local Screen = require('test.functional.ui.screen') describe('jobs', function() @@ -58,12 +64,12 @@ describe('jobs', function() it('changes to given / directory', function() nvim('command', "let g:job_opts.cwd = '/'") if iswin() then - nvim('command', "let j = jobstart('pwd|%{$_.Path}', g:job_opts)") + nvim('command', "let j = jobstart('(Get-Location).Path', g:job_opts)") else nvim('command', "let j = jobstart('pwd', g:job_opts)") end eq({'notification', 'stdout', - {0, {(iswin() and [[C:\]] or '/'), ''}}}, next_msg()) + {0, {pathroot(), ''}}}, next_msg()) eq({'notification', 'stdout', {0, {''}}}, next_msg()) eq({'notification', 'exit', {0, 0}}, next_msg()) end) @@ -73,13 +79,22 @@ describe('jobs', function() mkdir(dir) nvim('command', "let g:job_opts.cwd = '" .. dir .. "'") if iswin() then - nvim('command', "let j = jobstart('pwd|%{$_.Path}', g:job_opts)") + nvim('command', "let j = jobstart('(Get-Location).Path', g:job_opts)") else nvim('command', "let j = jobstart('pwd', g:job_opts)") end - eq({'notification', 'stdout', {0, {dir, ''}}}, next_msg()) - eq({'notification', 'stdout', {0, {''}}}, next_msg()) - eq({'notification', 'exit', {0, 0}}, next_msg()) + expect_msg_seq( + { {'notification', 'stdout', {0, {dir, ''} } }, + {'notification', 'stdout', {0, {''} } }, + {'notification', 'exit', {0, 0} } + }, + -- Alternative sequence: + { {'notification', 'stdout', {0, {dir} } }, + {'notification', 'stdout', {0, {'', ''} } }, + {'notification', 'stdout', {0, {''} } }, + {'notification', 'exit', {0, 0} } + } + ) rmdir(dir) end) @@ -104,13 +119,13 @@ describe('jobs', function() end) it('returns -1 when target is not executable #5465', function() - if helpers.pending_win32(pending) then return end local function new_job() return eval([[jobstart('')]]) end local executable_jobid = new_job() - local nonexecutable_jobid = eval( - "jobstart(['./test/functional/fixtures/non_executable.txt'])") + local nonexecutable_jobid = eval("jobstart(['"..(iswin() + and './test/functional/fixtures' + or './test/functional/fixtures/non_executable.txt').."'])") eq(-1, nonexecutable_jobid) -- Should _not_ throw an error. eq("", eval("v:errmsg")) @@ -122,11 +137,10 @@ describe('jobs', function() -- TODO: hangs on Windows if helpers.pending_win32(pending) then return end nvim('command', "let g:job_opts.on_stderr = function('OnEvent')") - nvim('command', "call jobstart('echo', g:job_opts)") + nvim('command', [[call jobstart('echo ""', g:job_opts)]]) expect_twostreams({{'notification', 'stdout', {0, {'', ''}}}, {'notification', 'stdout', {0, {''}}}}, {{'notification', 'stderr', {0, {''}}}}) - eq({'notification', 'exit', {0, 0}}, next_msg()) end) @@ -242,7 +256,6 @@ describe('jobs', function() end) it('will not leak memory if we leave a job running', function() - if helpers.pending_win32(pending) then return end -- TODO: Need `cat`. nvim('command', "call jobstart(['cat', '-'], g:job_opts)") end) @@ -283,19 +296,19 @@ describe('jobs', function() nvim('command', 'let g:job_opts.user = {"n": 5, "s": "str", "l": [1]}') nvim('command', [[call jobstart('echo "foo"', g:job_opts)]]) local data = {n = 5, s = 'str', l = {1}} - eq({'notification', 'stdout', {data, {'foo', ''}}}, next_msg()) - eq({'notification', 'stdout', {data, {''}}}, next_msg()) + expect_msg_seq( + { {'notification', 'stdout', {data, {'foo', ''}}}, + {'notification', 'stdout', {data, {''}}}, + }, + -- Alternative sequence: + { {'notification', 'stdout', {data, {'foo'}}}, + {'notification', 'stdout', {data, {'', ''}}}, + {'notification', 'stdout', {data, {''}}}, + } + ) eq({'notification', 'exit', {data, 0}}, next_msg()) end) - it('can omit options', function() - if helpers.pending_win32(pending) then return end - neq(0, nvim('eval', 'delete(".Xtestjob")')) - nvim('command', "call jobstart(['touch', '.Xtestjob'])") - nvim('command', "sleep 100m") - eq(0, nvim('eval', 'delete(".Xtestjob")')) - end) - it('can omit data callbacks', function() nvim('command', 'unlet g:job_opts.on_stdout') nvim('command', 'let g:job_opts.user = 5') @@ -307,8 +320,16 @@ describe('jobs', function() nvim('command', 'unlet g:job_opts.on_exit') nvim('command', 'let g:job_opts.user = 5') nvim('command', [[call jobstart('echo "foo"', g:job_opts)]]) - eq({'notification', 'stdout', {5, {'foo', ''}}}, next_msg()) - eq({'notification', 'stdout', {5, {''}}}, next_msg()) + expect_msg_seq( + { {'notification', 'stdout', {5, {'foo', ''} } }, + {'notification', 'stdout', {5, {''} } }, + }, + -- Alternative sequence: + { {'notification', 'stdout', {5, {'foo'} } }, + {'notification', 'stdout', {5, {'', ''} } }, + {'notification', 'stdout', {5, {''} } }, + } + ) end) it('will pass return code with the exit event', function() @@ -330,7 +351,6 @@ describe('jobs', function() end) it('can redefine callbacks being used by a job', function() - if helpers.pending_win32(pending) then return end -- TODO: Need `cat`. local screen = Screen.new() screen:attach() screen:set_default_attr_ids({ @@ -345,7 +365,7 @@ describe('jobs', function() \ 'on_stderr': function('g:JobHandler'), \ 'on_exit': function('g:JobHandler') \ } - let job = jobstart('cat -', g:callbacks) + let job = jobstart(['cat', '-'], g:callbacks) ]]) wait() source([[ @@ -410,7 +430,14 @@ describe('jobs', function() let g:job_opts = {'on_stdout': Callback} call jobstart('echo "some text"', g:job_opts) ]]) - eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg()) + expect_msg_seq( + { {'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, + }, + -- Alternative sequence: + { {'notification', '1', {'foo', 'bar', {'some text'}, 'stdout'}}, + {'notification', '1', {'foo', 'bar', {'', ''}, 'stdout'}}, + } + ) end) it('jobstart() works with closures', function() @@ -423,7 +450,14 @@ describe('jobs', function() let g:job_opts = {'on_stdout': MkFun()} call jobstart('echo "some text"', g:job_opts) ]]) - eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg()) + expect_msg_seq( + { {'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, + }, + -- Alternative sequence: + { {'notification', '1', {'foo', 'bar', {'some text'}, 'stdout'}}, + {'notification', '1', {'foo', 'bar', {'', ''}, 'stdout'}}, + } + ) end) it('jobstart() works when closure passed directly to `jobstart`', function() @@ -431,7 +465,14 @@ describe('jobs', function() let g:job_opts = {'on_stdout': {id, data, event -> rpcnotify(g:channel, '1', 'foo', 'bar', Normalize(data), event)}} call jobstart('echo "some text"', g:job_opts) ]]) - eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg()) + expect_msg_seq( + { {'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, + }, + -- Alternative sequence: + { {'notification', '1', {'foo', 'bar', {'some text'}, 'stdout'}}, + {'notification', '1', {'foo', 'bar', {'', ''}, 'stdout'}}, + } + ) end) describe('jobwait', function() @@ -490,7 +531,7 @@ describe('jobs', function() eq({'notification', 'wait', {{-3, 5}}}, next_msg()) end) - it('will return -2 when interrupted', function() + it('will return -2 when interrupted without timeout', function() feed_command('call rpcnotify(g:channel, "ready") | '.. 'call rpcnotify(g:channel, "wait", '.. 'jobwait([jobstart("sleep 10; exit 55")]))') @@ -499,6 +540,15 @@ describe('jobs', function() eq({'notification', 'wait', {{-2}}}, next_msg()) end) + it('will return -2 when interrupted with timeout', function() + feed_command('call rpcnotify(g:channel, "ready") | '.. + 'call rpcnotify(g:channel, "wait", '.. + 'jobwait([jobstart("sleep 10; exit 55")], 10000))') + eq({'notification', 'ready', {}}, next_msg()) + feed('<c-c>') + eq({'notification', 'wait', {{-2}}}, next_msg()) + end) + it('can be called recursively', function() if helpers.pending_win32(pending) then return end -- TODO: Need `cat`. source([[ @@ -590,6 +640,57 @@ describe('jobs', function() ok(string.find(err, "E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set") ~= nil) end) + it('does not crash when repeatedly failing to start shell', function() + source([[ + set shell=nosuchshell + func! DoIt() + call jobstart('true') + call jobstart('true') + endfunc + ]]) + -- The crash only triggered if both jobs are cleaned up on the same event + -- loop tick. This is also prevented by try-block, so feed must be used. + feed_command("call DoIt()") + feed('<cr>') -- press RETURN + eq(2,eval('1+1')) + end) + + it('jobstop() kills entire process tree #6530', function() + command('set shell& shellcmdflag& shellquote& shellpipe& shellredir& shellxquote&') + + -- XXX: Using `nvim` isn't a good test, it reaps its children on exit. + -- local c = 'call jobstart([v:progpath, "-u", "NONE", "-i", "NONE", "--headless"])' + -- local j = eval("jobstart([v:progpath, '-u', 'NONE', '-i', 'NONE', '--headless', '-c', '" + -- ..c.."', '-c', '"..c.."'])") + + -- Create child with several descendants. + local j = (iswin() + and eval([=[jobstart('start /b cmd /c "ping 127.0.0.1 -n 1 -w 30000 > NUL"]=] + ..[=[ & start /b cmd /c "ping 127.0.0.1 -n 1 -w 40000 > NUL"]=] + ..[=[ & start /b cmd /c "ping 127.0.0.1 -n 1 -w 50000 > NUL"')]=]) + or eval("jobstart('sleep 30 | sleep 30 | sleep 30')")) + local ppid = funcs.jobpid(j) + local children + retry(nil, nil, function() + children = meths.get_proc_children(ppid) + eq(3, #children) + end) + -- Assert that nvim_get_proc() sees the children. + for _, child_pid in ipairs(children) do + local info = meths.get_proc(child_pid) + -- eq((iswin() and 'nvim.exe' or 'nvim'), info.name) + eq(ppid, info.ppid) + end + -- Kill the root of the tree. + funcs.jobstop(j) + -- Assert that the children were killed. + retry(nil, nil, function() + for _, child_pid in ipairs(children) do + eq(NIL, meths.get_proc(child_pid)) + end + end) + end) + describe('running tty-test program', function() if helpers.pending_win32(pending) then return end local function next_chunk() diff --git a/test/functional/eval/api_functions_spec.lua b/test/functional/eval/api_functions_spec.lua index fea4a87a26..6f440c7d82 100644 --- a/test/functional/eval/api_functions_spec.lua +++ b/test/functional/eval/api_functions_spec.lua @@ -106,7 +106,8 @@ describe('api functions', function() it('have metadata accessible with api_info()', function() local api_keys = eval("sort(keys(api_info()))") - eq({'error_types', 'functions', 'types', 'ui_events', 'version'}, api_keys) + eq({'error_types', 'functions', 'types', + 'ui_events', 'ui_options', 'version'}, api_keys) end) it('are highlighted by vim.vim syntax file', function() diff --git a/test/functional/eval/backtick_expansion_spec.lua b/test/functional/eval/backtick_expansion_spec.lua index 81e8e295fa..b1b44cfa8b 100644 --- a/test/functional/eval/backtick_expansion_spec.lua +++ b/test/functional/eval/backtick_expansion_spec.lua @@ -21,11 +21,19 @@ describe("backtick expansion", function() end) it("with default 'shell'", function() - if helpers.pending_win32(pending) then return end -- Need win32 shell fixes - command(":silent args `echo ***2`") + if helpers.iswin() then + command(":silent args `dir /b *2`") + else + command(":silent args `echo ***2`") + end eq({ "file2", }, eval("argv()")) - command(":silent args `echo */*4`") - eq({ "subdir/file4", }, 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() diff --git a/test/functional/eval/buf_functions_spec.lua b/test/functional/eval/buf_functions_spec.lua index db50874c53..7de58766b9 100644 --- a/test/functional/eval/buf_functions_spec.lua +++ b/test/functional/eval/buf_functions_spec.lua @@ -14,6 +14,7 @@ local curbufmeths = helpers.curbufmeths local curwinmeths = helpers.curwinmeths local curtabmeths = helpers.curtabmeths local get_pathsep = helpers.get_pathsep +local rmdir = helpers.rmdir local fname = 'Xtest-functional-eval-buf_functions' local fname2 = fname .. '.2' @@ -61,7 +62,7 @@ describe('bufname() function', function() lfs.mkdir(dirname) end) after_each(function() - lfs.rmdir(dirname) + rmdir(dirname) end) it('returns expected buffer name', function() eq('', funcs.bufname('%')) -- Buffer has no name yet @@ -143,7 +144,7 @@ describe('bufwinnr() function', function() lfs.mkdir(dirname) end) after_each(function() - lfs.rmdir(dirname) + rmdir(dirname) end) it('returns expected window number', function() eq(1, funcs.bufwinnr('%')) diff --git a/test/functional/eval/execute_spec.lua b/test/functional/eval/execute_spec.lua index 91966ed3dd..925e311c7d 100644 --- a/test/functional/eval/execute_spec.lua +++ b/test/functional/eval/execute_spec.lua @@ -9,6 +9,7 @@ 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) @@ -105,22 +106,30 @@ describe('execute()', function() end) it('does not corrupt the command display #5422', function() - local screen = Screen.new(70, 5) + local screen = Screen.new(70, 7) screen:attach() feed(':echo execute("hi ErrorMsg")<CR>') screen:expect([[ - ~ | - ~ | + | + {1:~ }| + {1:~ }| + {2: }| :echo execute("hi ErrorMsg") | ErrorMsg xxx ctermfg=15 ctermbg=1 guifg=White guibg=Red | - Press ENTER or type command to continue^ | - ]]) + {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('<CR>') end) - -- This matches Vim behavior. - it('does not capture shell-command output', function() - eq('\n:!echo "foo"\13\n', funcs.execute('!echo "foo"')) + -- This deviates from vim behavior, but is consistent + -- with how nvim currently displays the output. + it('does capture 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() diff --git a/test/functional/eval/fnamemodify_spec.lua b/test/functional/eval/fnamemodify_spec.lua new file mode 100644 index 0000000000..fe6b50a544 --- /dev/null +++ b/test/functional/eval/fnamemodify_spec.lua @@ -0,0 +1,39 @@ +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 command = helpers.command +local write_file = helpers.write_file + +describe('fnamemodify()', function() + setup(function() + write_file('Xtest-fnamemodify.txt', [[foobar]]) + end) + + before_each(clear) + + teardown(function() + os.remove('Xtest-fnamemodify.txt') + end) + + it('works', 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) +end) diff --git a/test/functional/eval/getline_spec.lua b/test/functional/eval/getline_spec.lua new file mode 100644 index 0000000000..3c56bde094 --- /dev/null +++ b/test/functional/eval/getline_spec.lua @@ -0,0 +1,39 @@ +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/has_spec.lua b/test/functional/eval/has_spec.lua index 78c4e08fde..a3af2d1a20 100644 --- a/test/functional/eval/has_spec.lua +++ b/test/functional/eval/has_spec.lua @@ -57,4 +57,10 @@ describe('has()', function() 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 index 6d5b64b929..6112cf64e3 100644 --- a/test/functional/eval/hostname_spec.lua +++ b/test/functional/eval/hostname_spec.lua @@ -1,7 +1,9 @@ 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) @@ -11,7 +13,8 @@ describe('hostname()', function() ok(string.len(actual) > 0) if call('executable', 'hostname') == 1 then local expected = string.gsub(call('system', 'hostname'), '[\n\r]', '') - helpers.eq(expected, actual) + 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 index 1e6b107c60..777f49462d 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -58,6 +58,7 @@ before_each(function() RBP2={background=Screen.colors.Yellow}, RBP3={background=Screen.colors.Green}, RBP4={background=Screen.colors.Blue}, + SEP={bold = true, reverse = true}, }) end) @@ -65,9 +66,9 @@ describe('input()', function() it('works with multiline prompts', function() feed([[:call input("Test\nFoo")<CR>]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| Test | Foo^ | ]]) @@ -75,9 +76,9 @@ describe('input()', function() it('works with multiline prompts and :echohl', function() feed([[:echohl Test | call input("Test\nFoo")<CR>]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| {T:Test} | {T:Foo}^ | ]]) @@ -242,17 +243,17 @@ describe('input()', function() it('is not hidden by :silent', function() feed([[:silent call input('Foo: ')<CR>]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| Foo: ^ | | ]]) feed('Bar') screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| Foo: Bar^ | | ]]) @@ -263,9 +264,9 @@ describe('inputdialog()', function() it('works with multiline prompts', function() feed([[:call inputdialog("Test\nFoo")<CR>]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| Test | Foo^ | ]]) @@ -273,9 +274,9 @@ describe('inputdialog()', function() it('works with multiline prompts and :echohl', function() feed([[:echohl Test | call inputdialog("Test\nFoo")<CR>]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| {T:Test} | {T:Foo}^ | ]]) diff --git a/test/functional/eval/interrupt_spec.lua b/test/functional/eval/interrupt_spec.lua new file mode 100644 index 0000000000..7f4ca95317 --- /dev/null +++ b/test/functional/eval/interrupt_spec.lua @@ -0,0 +1,61 @@ +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 wait = helpers.wait +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()<CR>:let t_bl = copy(bl)<CR>') + sleep(min_dur / 16 * 1000) + feed('<C-c>') + wait() + 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()<CR>:let t_j = join(bl)<CR>') + sleep(min_dur / 16 * 1000) + feed('<C-c>') + wait() + 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/match_functions_spec.lua b/test/functional/eval/match_functions_spec.lua index 3150d89f62..7989b22b5e 100644 --- a/test/functional/eval/match_functions_spec.lua +++ b/test/functional/eval/match_functions_spec.lua @@ -1,9 +1,11 @@ local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') local eq = helpers.eq local clear = helpers.clear local funcs = helpers.funcs local command = helpers.command +local exc_exec = helpers.exc_exec before_each(clear) @@ -59,3 +61,95 @@ describe('matchadd()', function() }}, funcs.getmatches()) end) end) + +describe('matchaddpos()', function() + it('errors out on invalid input', function() + command('hi clear PreProc') + eq('Vim(let):E5030: Empty list at position 0', + exc_exec('let val = matchaddpos("PreProc", [[]])')) + eq('Vim(let):E5030: Empty list at position 1', + exc_exec('let val = matchaddpos("PreProc", [1, v:_null_list])')) + eq('Vim(let):E5031: List or number required at position 1', + exc_exec('let val = matchaddpos("PreProc", [1, v:_null_dict])')) + end) + it('works with 0 lnum', function() + command('hi clear PreProc') + eq(4, funcs.matchaddpos('PreProc', {1}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1}, + priority=3, + id=4, + }}, funcs.getmatches()) + funcs.matchdelete(4) + eq(4, funcs.matchaddpos('PreProc', {{0}, 1}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1}, + priority=3, + id=4, + }}, funcs.getmatches()) + funcs.matchdelete(4) + eq(4, funcs.matchaddpos('PreProc', {0, 1}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1}, + priority=3, + id=4, + }}, funcs.getmatches()) + end) + it('works with negative numbers', function() + command('hi clear PreProc') + eq(4, funcs.matchaddpos('PreProc', {-10, 1}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1}, + priority=3, + id=4, + }}, funcs.getmatches()) + funcs.matchdelete(4) + eq(4, funcs.matchaddpos('PreProc', {{-10}, 1}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1}, + priority=3, + id=4, + }}, funcs.getmatches()) + funcs.matchdelete(4) + eq(4, funcs.matchaddpos('PreProc', {{2, -1}, 1}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1}, + priority=3, + id=4, + }}, funcs.getmatches()) + funcs.matchdelete(4) + eq(4, funcs.matchaddpos('PreProc', {{2, 0, -1}, 1}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1}, + priority=3, + id=4, + }}, funcs.getmatches()) + end) + it('works with zero length', function() + local screen = Screen.new(40, 5) + screen:attach() + funcs.setline(1, 'abcdef') + command('hi PreProc guifg=Red') + eq(4, funcs.matchaddpos('PreProc', {{1, 2, 0}}, 3, 4)) + eq({{ + group='PreProc', + pos1 = {1, 2, 0}, + priority=3, + id=4, + }}, funcs.getmatches()) + screen:expect([[ + ^a{1:b}cdef | + {2:~ }| + {2:~ }| + {2:~ }| + | + ]], {[1] = {foreground = Screen.colors.Red}, [2] = {bold = true, foreground = Screen.colors.Blue1}}) + end) +end) diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua index b241635dfe..a8a413f68b 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -463,7 +463,8 @@ describe('msgpackparse() function', function() eval(cmd) eval(cmd) -- do it again (try to force segfault) local api_info = eval(cmd) -- do it again - eq({'error_types', 'functions', 'types', 'ui_events', 'version'}, api_info) + eq({'error_types', 'functions', 'types', + 'ui_events', 'ui_options', 'version'}, api_info) end) it('fails when called with no arguments', function() @@ -628,7 +629,7 @@ describe('msgpackdump() function', function() it('fails to dump a recursive (key) map in a special dict', function() command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('call add(todump._VAL, [todump, 0])') - eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 1', + eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0', exc_exec('call msgpackdump([todump])')) end) diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 6fd30caec9..afe999e1fa 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -41,43 +41,9 @@ describe('NULL', function() end describe('list', function() -- Incorrect behaviour - - -- FIXME map() should not return 0 without error - null_expr_test('does not crash map()', 'map(L, "v:val")', 0, 0) - -- FIXME map() should not return 0 without error - null_expr_test('does not crash filter()', 'filter(L, "1")', 0, 0) - -- FIXME map() should at least return L - null_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 0) - -- FIXME filter() should at least return L - null_expr_test('makes filter() return v:_null_list', 'map(L, "1") is# L', 0, 0) - -- FIXME add() should not return 1 at all - null_expr_test('does not crash add()', 'add(L, 0)', 0, 1) - null_expr_test('does not crash extend()', 'extend(L, [1])', 'E742: Cannot change value of extend() argument', 0) - null_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, {1}) - -- FIXME should be accepted by inputlist() - null_expr_test('is accepted as an empty list by inputlist()', - '[feedkeys("\\n"), inputlist(L)]', 'E686: Argument of inputlist() must be a List', {0, 0}) - -- FIXME should be accepted by writefile(), return {0, {}} - null_expr_test('is accepted as an empty list by writefile()', - ('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname), - 'E484: Can\'t open file ' .. tmpfname, {0, {}}) - -- FIXME should give error message - null_expr_test('does not crash remove()', 'remove(L, 0)', 0, 0) - -- FIXME should return 0 - null_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, -1) - -- FIXME should return 0 - null_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, -1) - -- FIXME should return 0 - null_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, -1) - -- FIXME should return empty list or error out - null_expr_test('is accepted by sort()', 'sort(L)', 0, 0) - -- FIXME Should return 1 - null_expr_test('is accepted by sort()', 'sort(L) is L', 0, 0) - -- FIXME should not error out - null_test('is accepted by :cexpr', 'cexpr L', 'Vim(cexpr):E777: String or List expected') - -- FIXME should not error out - null_test('is accepted by :lexpr', 'lexpr L', 'Vim(lexpr):E777: String or List expected') - null_test('is accepted by :for', 'for x in L|throw x|endfor', 0) + -- FIXME Should error out with different message + null_test('makes :unlet act as if it is not a list', ':unlet L[0]', + 'Vim(unlet):E689: Can only index a List or Dictionary') -- Subjectable behaviour @@ -85,20 +51,19 @@ describe('NULL', function() null_expr_test('is equal to empty list', 'L == []', 0, 0) -- FIXME Should return 1 null_expr_test('is equal to empty list (reverse order)', '[] == L', 0, 0) - -- FIXME Should return 1 - null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) - - -- Crashes - - -- null_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0) - -- null_expr_test('does not crash setline', 'setline(1, L)', 0, 0) - -- null_expr_test('does not crash system()', 'system("cat", L)', 0, '') - -- null_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {}) -- Correct behaviour + null_expr_test('can be indexed with error message for empty list', 'L[0]', + 'E684: list index out of range: 0\nE15: Invalid expression: L[0]', nil) + null_expr_test('can be splice-indexed', 'L[:]', 0, {}) + null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) + null_test('is accepted by :for', 'for x in L|throw x|endfor', 0) null_expr_test('does not crash append()', 'append(1, L)', 0, 0, function() eq({''}, curbufmeths.get_lines(0, -1, false)) end) + null_expr_test('does not crash setline()', 'setline(1, L)', 0, 0, function() + eq({''}, curbufmeths.get_lines(0, -1, false)) + end) null_expr_test('is identical to itself', 'L is L', 0, 1) null_expr_test('can be sliced', 'L[:]', 0, {}) null_expr_test('can be copied', 'copy(L)', 0, {}) @@ -111,6 +76,8 @@ describe('NULL', function() null_expr_test('does not crash line()', 'line(L)', 0, 0) null_expr_test('does not crash count()', 'count(L, 1)', 0, 0) null_expr_test('does not crash cursor()', 'cursor(L)', 'E474: Invalid argument', -1) + null_expr_test('does not crash map()', 'map(L, "v:val")', 0, {}) + null_expr_test('does not crash filter()', 'filter(L, "1")', 0, {}) null_expr_test('is empty', 'empty(L)', 0, 1) null_expr_test('does not crash get()', 'get(L, 1, 10)', 0, 10) null_expr_test('has zero length', 'len(L)', 0, 0) @@ -126,6 +93,44 @@ describe('NULL', function() null_expr_test('is equal to itself', 'L == L', 0, 1) null_expr_test('is not not equal to itself', 'L != L', 0, 0) null_expr_test('counts correctly', 'count([L], L)', 0, 1) + null_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 1) + null_expr_test('makes filter() return v:_null_list', 'filter(L, "1") is# L', 0, 1) + null_test('is treated by :let as empty list', ':let [l] = L', 'Vim(let):E688: More targets than List items') + null_expr_test('is accepted as an empty list by inputlist()', '[feedkeys("\\n"), inputlist(L)]', + 'Type number and <Enter> or click with mouse (empty cancels): ', {0, 0}) + null_expr_test('is accepted as an empty list by writefile()', + ('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname), + 0, {0, {}}) + null_expr_test('makes add() error out', 'add(L, 0)', + 'E742: Cannot change value of add() argument', 1) + null_expr_test('makes insert() error out', 'insert(L, 1)', + 'E742: Cannot change value of insert() argument', 0) + null_expr_test('does not crash remove()', 'remove(L, 0)', + 'E742: Cannot change value of remove() argument', 0) + null_expr_test('makes reverse() error out', 'reverse(L)', + 'E742: Cannot change value of reverse() argument', 0) + null_expr_test('makes sort() error out', 'sort(L)', + 'E742: Cannot change value of sort() argument', 0) + null_expr_test('makes uniq() error out', 'uniq(L)', + 'E742: Cannot change value of uniq() argument', 0) + null_expr_test('does not crash extend()', 'extend(L, [1])', 'E742: Cannot change value of extend() argument', 0) + null_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, {1}) + null_expr_test('makes join() return empty string', 'join(L, "")', 0, '') + null_expr_test('makes msgpackdump() return empty list', 'msgpackdump(L)', 0, {}) + null_expr_test('does not crash system()', 'system("cat", L)', 0, '') + null_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0) + null_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {}) + null_test('does not make Neovim crash when v:oldfiles gets assigned to that', ':let v:oldfiles = L|oldfiles', 0) + null_expr_test('does not make complete() crash or error out', + 'execute(":normal i\\<C-r>=complete(1, L)[-1]\\n")', + '', '\n', function() + eq({''}, curbufmeths.get_lines(0, -1, false)) + end) + null_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, 0) + null_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, 0) + null_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, 0) + null_test('is accepted by :cexpr', 'cexpr L', 0) + null_test('is accepted by :lexpr', 'lexpr L', 0) end) describe('dict', function() it('does not crash when indexing NULL dict', function() @@ -134,5 +139,9 @@ describe('NULL', function() end) null_expr_test('makes extend error out', 'extend(D, {})', 'E742: Cannot change value of extend() argument', 0) null_expr_test('makes extend do nothing', 'extend({1: 2}, D)', 0, {['1']=2}) + null_expr_test('does not crash map()', 'map(D, "v:val")', 0, {}) + null_expr_test('does not crash filter()', 'filter(D, "1")', 0, {}) + null_expr_test('makes map() return v:_null_dict', 'map(D, "v:val") is# D', 0, 1) + null_expr_test('makes filter() return v:_null_dict', 'filter(D, "1") is# D', 0, 1) end) end) diff --git a/test/functional/eval/server_spec.lua b/test/functional/eval/server_spec.lua index 393616838e..4e4aed864b 100644 --- a/test/functional/eval/server_spec.lua +++ b/test/functional/eval/server_spec.lua @@ -1,31 +1,40 @@ - local helpers = require('test.functional.helpers')(after_each) local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval local command = helpers.command local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths -local os_name = helpers.os_name +local iswin = helpers.iswin +local ok = helpers.ok +local matches = helpers.matches local function clear_serverlist() - for _, server in pairs(funcs.serverlist()) do - funcs.serverstop(server) - end + for _, server in pairs(funcs.serverlist()) do + funcs.serverstop(server) + end end -describe('serverstart(), serverstop()', function() +describe('server', function() before_each(clear) - it('sets $NVIM_LISTEN_ADDRESS on first invocation', function() + it('serverstart() sets $NVIM_LISTEN_ADDRESS on first invocation', function() -- Unset $NVIM_LISTEN_ADDRESS command('let $NVIM_LISTEN_ADDRESS = ""') local s = eval('serverstart()') assert(s ~= nil and s:len() > 0, "serverstart() returned empty") eq(s, eval('$NVIM_LISTEN_ADDRESS')) - command("call serverstop('"..s.."')") + eq(1, eval("serverstop('"..s.."')")) eq('', eval('$NVIM_LISTEN_ADDRESS')) end) - it('sets v:servername _only_ on nvim startup unless all servers are stopped', + it('sets new v:servername if $NVIM_LISTEN_ADDRESS is invalid', function() + clear({env={NVIM_LISTEN_ADDRESS='.'}}) + eq('.', eval('$NVIM_LISTEN_ADDRESS')) + local servers = funcs.serverlist() + eq(1, #servers) + ok(string.len(servers[1]) > 4) -- Like /tmp/nvim…/… or \\.\pipe\… + end) + + it('sets v:servername at startup or if all servers were stopped', function() local initial_server = meths.get_vvar('servername') assert(initial_server ~= nil and initial_server:len() > 0, @@ -38,24 +47,23 @@ describe('serverstart(), serverstop()', function() neq(initial_server, s) -- serverstop() does _not_ modify v:servername... - funcs.serverstop(s) + eq(1, funcs.serverstop(s)) eq(initial_server, meths.get_vvar('servername')) -- ...unless we stop _all_ servers. - funcs.serverstop(funcs.serverlist()[1]) + eq(1, funcs.serverstop(funcs.serverlist()[1])) eq('', meths.get_vvar('servername')) -- v:servername will take the next available server. - local servername = (os_name() == 'windows' - and [[\\.\pipe\Xtest-functional-server-pipe]] - or 'Xtest-functional-server-socket') + local servername = (iswin() and [[\\.\pipe\Xtest-functional-server-pipe]] + or 'Xtest-functional-server-socket') funcs.serverstart(servername) eq(servername, meths.get_vvar('servername')) end) - it('serverstop() ignores invalid input', function() - command("call serverstop('')") - command("call serverstop('bogus-socket-name')") + it('serverstop() returns false for invalid input', function() + eq(0, eval("serverstop('')")) + eq(0, eval("serverstop('bogus-socket-name')")) end) it('parses endpoints correctly', function() @@ -96,17 +104,13 @@ describe('serverstart(), serverstop()', function() funcs.serverstart('127.0.0.1:65536') -- invalid port eq({}, funcs.serverlist()) end) -end) - -describe('serverlist()', function() - before_each(clear) - it('returns the list of servers', function() + it('serverlist() returns the list of servers', function() -- There should already be at least one server. local n = eval('len(serverlist())') - -- Add a few - local servs = (os_name() == 'windows' + -- Add some servers. + local servs = (iswin() and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] } or { [[Xtest-pipe0934]], [[Xtest-pipe4324]] }) for _, s in ipairs(servs) do @@ -120,9 +124,31 @@ describe('serverlist()', function() -- The new servers should be at the end of the list. for i = 1, #servs do eq(servs[i], new_servs[i + n]) - command("call serverstop('"..servs[i].."')") + eq(1, eval("serverstop('"..servs[i].."')")) end -- After serverstop() the servers should NOT be in the list. eq(n, eval('len(serverlist())')) end) end) + +describe('startup --listen', function() + it('validates', function() + clear() + + local cmd = { unpack(helpers.nvim_argv) } + table.insert(cmd, '--listen') + matches('nvim.*: Argument missing after: "%-%-listen"', funcs.system(cmd)) + + cmd = { unpack(helpers.nvim_argv) } + table.insert(cmd, '--listen2') + matches('nvim.*: Garbage after option argument: "%-%-listen2"', funcs.system(cmd)) + end) + + it('sets v:servername, overrides $NVIM_LISTEN_ADDRESS', function() + local addr = (iswin() and [[\\.\pipe\Xtest-listen-pipe]] + or 'Xtest-listen-pipe') + clear({ env={ NVIM_LISTEN_ADDRESS='Xtest-env-pipe' }, + args={ '--listen', addr } }) + eq(addr, meths.get_vvar('servername')) + end) +end) diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 7e213e2156..201426c40b 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -5,6 +5,7 @@ local eq, call, clear, eval, feed_command, feed, nvim = helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.feed_command, helpers.feed, helpers.nvim local command = helpers.command +local exc_exec = helpers.exc_exec local iswin = helpers.iswin local Screen = require('test.functional.ui.screen') @@ -89,7 +90,9 @@ describe('system()', function() end) it('does NOT run in shell', function() - if not iswin() then + if iswin() then + eq("%PATH%\n", eval("system(['powershell', '-NoProfile', '-NoLogo', '-ExecutionPolicy', 'RemoteSigned', '-Command', 'echo', '%PATH%'])")) + else eq("* $PATH %PATH%\n", eval("system(['echo', '*', '$PATH', '%PATH%'])")) end end) @@ -117,33 +120,47 @@ describe('system()', function() end end) - describe('executes shell function if passed a string', function() + describe('executes shell function', function() local screen before_each(function() - clear() - screen = Screen.new() - screen:attach() + clear() + screen = Screen.new() + screen:attach() end) after_each(function() - screen:detach() + screen:detach() end) if iswin() then + local function test_more() + eq('root = true', eval([[get(split(system('"more" ".editorconfig"'), "\n"), 0, '')]])) + end + local function test_shell_unquoting() + eval([[system('"ping" "-n" "1" "127.0.0.1"')]]) + eq(0, eval('v:shell_error')) + eq('"a b"\n', eval([[system('cmd /s/c "cmd /s/c "cmd /s/c "echo "a b""""')]])) + eq('"a b"\n', eval([[system('powershell -NoProfile -NoLogo -ExecutionPolicy RemoteSigned -Command echo ''\^"a b\^"''')]])) + end + it('with shell=cmd.exe', function() command('set shell=cmd.exe') eq('""\n', eval([[system('echo ""')]])) eq('"a b"\n', eval([[system('echo "a b"')]])) eq('a \nb\n', eval([[system('echo a & echo b')]])) eq('a \n', eval([[system('echo a 2>&1')]])) + test_more() eval([[system('cd "C:\Program Files"')]]) eq(0, eval('v:shell_error')) + test_shell_unquoting() end) it('with shell=cmd', function() command('set shell=cmd') eq('"a b"\n', eval([[system('echo "a b"')]])) + test_more() + test_shell_unquoting() end) it('with shell=$COMSPEC', function() @@ -151,6 +168,8 @@ describe('system()', function() if comspecshell == 'cmd.exe' then command('set shell=$COMSPEC') eq('"a b"\n', eval([[system('echo "a b"')]])) + test_more() + test_shell_unquoting() else pending('$COMSPEC is not cmd.exe: ' .. comspecshell) end @@ -184,8 +203,10 @@ describe('system()', function() ]]) end) - it('`yes` and is interrupted with CTRL-C', function() - feed(':call system("yes")<cr>') + it('`yes` interrupted with CTRL-C', function() + feed(':call system("' .. (iswin() + and 'for /L %I in (1,0,2) do @echo y' + or 'yes') .. '")<cr>') screen:expect([[ | ~ | @@ -200,8 +221,11 @@ describe('system()', function() ~ | ~ | ~ | - :call system("yes") | - ]]) +]] .. (iswin() + and [[ + :call system("for /L %I in (1,0,2) do @echo y") |]] + or [[ + :call system("yes") |]])) feed('<c-c>') screen:expect([[ ^ | @@ -231,6 +255,8 @@ describe('system()', function() end end) it('to backgrounded command does not crash', function() + -- cmd.exe doesn't background a command with & + if iswin() then return end -- This is indeterminate, just exercise the codepath. May get E5677. feed_command('call system("echo -n echoed &")') local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") @@ -246,14 +272,20 @@ describe('system()', function() eq("input", eval('system("cat -", "input")')) end) it('to backgrounded command does not crash', function() + -- cmd.exe doesn't background a command with & + if iswin() then return end -- This is indeterminate, just exercise the codepath. May get E5677. - feed_command('call system("cat - &")') + feed_command('call system("cat - &", "input")') local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") if v_errnum then eq("E5677:", v_errnum) end eq(2, eval("1+1")) -- Still alive? end) + it('works with an empty string', function() + eq("test\n", eval('system("echo test", "")')) + eq(2, eval("1+1")) -- Still alive? + end) end) describe('passing a lot of input', function() @@ -271,9 +303,12 @@ describe('system()', function() end) end) - describe('input passed as Number', function() - it('stringifies the input', function() - eq('1', eval('system("cat", 1)')) + describe('Number input', function() + it('is treated as a buffer id', function() + command("put ='text in buffer 1'") + eq('\ntext in buffer 1\n', eval('system("cat", 1)')) + eq('Vim(echo):E86: Buffer 42 does not exist', + exc_exec('echo system("cat", 42)')) end) end) @@ -284,7 +319,7 @@ describe('system()', function() after_each(delete_file(fname)) it('replaces NULs by SOH characters', function() - eq('part1\001part2\001part3\n', eval('system("cat '..fname..'")')) + eq('part1\001part2\001part3\n', eval([[system('"cat" "]]..fname..[["')]])) end) end) @@ -351,7 +386,7 @@ describe('systemlist()', function() end end) - describe('exectues shell function', function() + describe('executes shell function', function() local screen before_each(function() @@ -384,7 +419,7 @@ describe('systemlist()', function() ]]) end) - it('`yes` and is interrupted with CTRL-C', function() + it('`yes` interrupted with CTRL-C', function() feed(':call systemlist("yes | xargs")<cr>') screen:expect([[ | @@ -442,12 +477,14 @@ describe('systemlist()', function() describe('with output containing NULs', function() local fname = 'Xtest' - before_each(create_file_with_nuls(fname)) + before_each(function() + command('set ff=unix') + create_file_with_nuls(fname)() + end) after_each(delete_file(fname)) it('replaces NULs by newline characters', function() - if helpers.pending_win32(pending) then return end - eq({'part1\npart2\npart3'}, eval('systemlist("cat '..fname..'")')) + eq({'part1\npart2\npart3'}, eval([[systemlist('"cat" "]]..fname..[["')]])) end) end) diff --git a/test/functional/ex_cmds/bang_filter_spec.lua b/test/functional/ex_cmds/bang_filter_spec.lua deleted file mode 100644 index aaec983b73..0000000000 --- a/test/functional/ex_cmds/bang_filter_spec.lua +++ /dev/null @@ -1,51 +0,0 @@ --- Specs for bang/filter commands - -local helpers = require('test.functional.helpers')(after_each) -local feed, command, clear = helpers.feed, helpers.command, helpers.clear -local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir - -if helpers.pending_win32(pending) then return end - -local Screen = require('test.functional.ui.screen') - - -describe('issues', function() - local screen - - before_each(function() - clear() - rmdir('bang_filter_spec') - mkdir('bang_filter_spec') - write_file('bang_filter_spec/f1', 'f1') - write_file('bang_filter_spec/f2', 'f2') - write_file('bang_filter_spec/f3', 'f3') - screen = Screen.new() - screen:attach() - end) - - after_each(function() - rmdir('bang_filter_spec') - end) - - it('#3269 Last line of shell output is not truncated', function() - command([[nnoremap <silent>\l :!ls bang_filter_spec<cr>]]) - feed([[\l]]) - screen:expect([[ - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - :!ls bang_filter_spec | - | - f1 | - f2 | - f3 | - Press ENTER or type command to continue^ | - ]]) - end) - -end) diff --git a/test/functional/ex_cmds/cd_spec.lua b/test/functional/ex_cmds/cd_spec.lua index 059cb26d5d..bc2b365b30 100644 --- a/test/functional/ex_cmds/cd_spec.lua +++ b/test/functional/ex_cmds/cd_spec.lua @@ -8,8 +8,7 @@ local call = helpers.call local clear = helpers.clear local command = helpers.command local exc_exec = helpers.exc_exec - -if helpers.pending_win32(pending) then return end +local pathsep = helpers.get_pathsep() -- These directories will be created for testing local directories = { @@ -75,8 +74,8 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, lwd(globalwin, tabnr)) -- Window with local dir reports as such - eq(globalDir .. '/' .. directories.window, cwd(localwin)) - eq(globalDir .. '/' .. directories.window, cwd(localwin, tabnr)) + eq(globalDir .. pathsep .. directories.window, cwd(localwin)) + eq(globalDir .. pathsep .. directories.window, cwd(localwin, tabnr)) eq(1, lwd(localwin)) eq(1, lwd(localwin, tabnr)) @@ -86,7 +85,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, lwd(globalwin, tabnr)) -- From new tab page, local window reports as such - eq(globalDir .. '/' .. directories.window, cwd(localwin, tabnr)) + eq(globalDir .. pathsep .. directories.window, cwd(localwin, tabnr)) eq(1, lwd(localwin, tabnr)) end) @@ -109,14 +108,14 @@ for _, cmd in ipairs {'cd', 'chdir'} do eq(0, lwd(-1, globaltab)) -- new tab reports local - eq(globalDir .. '/' .. directories.tab, cwd(-1, 0)) - eq(globalDir .. '/' .. directories.tab, cwd(-1, localtab)) + eq(globalDir .. pathsep .. directories.tab, cwd(-1, 0)) + eq(globalDir .. pathsep .. directories.tab, cwd(-1, localtab)) eq(1, lwd(-1, 0)) eq(1, lwd(-1, localtab)) command('tabnext') -- From original tab page, local reports as such - eq(globalDir .. '/' .. directories.tab, cwd(-1, localtab)) + eq(globalDir .. pathsep .. directories.tab, cwd(-1, localtab)) eq(1, lwd(-1, localtab)) end) end) @@ -147,17 +146,17 @@ for _, cmd in ipairs {'cd', 'chdir'} do -- Create a new tab and change directory command('tabnew') command('silent t' .. cmd .. ' ' .. directories.tab) - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) -- Create a new tab and verify it has inherited the directory command('tabnew') - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) -- Change tab and change back, verify that directories are correct command('tabnext') eq(globalDir, tcwd()) command('tabprevious') - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) end) end) @@ -173,7 +172,7 @@ for _, cmd in ipairs {'cd', 'chdir'} do -- Change tab-local working directory and verify it is different command('silent t' .. cmd .. ' ' .. directories.tab) - eq(globalDir .. '/' .. directories.tab, cwd()) + eq(globalDir .. pathsep .. directories.tab, cwd()) eq(cwd(), tcwd()) -- working directory maches tab directory eq(1, tlwd()) eq(cwd(), wcwd()) -- still no window-directory @@ -183,16 +182,16 @@ for _, cmd in ipairs {'cd', 'chdir'} do command('new') eq(1, tlwd()) -- Still tab-local working directory eq(0, wlwd()) -- Still no window-local working directory - eq(globalDir .. '/' .. directories.tab, cwd()) + eq(globalDir .. pathsep .. directories.tab, cwd()) command('silent l' .. cmd .. ' ../' .. directories.window) - eq(globalDir .. '/' .. directories.window, cwd()) - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.window, cwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) eq(1, wlwd()) -- Verify the first window still has the tab local directory command('wincmd w') - eq(globalDir .. '/' .. directories.tab, cwd()) - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, cwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) eq(0, wlwd()) -- No window-local directory -- Change back to initial tab and verify working directory has stayed @@ -203,10 +202,10 @@ for _, cmd in ipairs {'cd', 'chdir'} do -- Verify global changes don't affect local ones command('silent ' .. cmd .. ' ' .. directories.global) - eq(globalDir .. '/' .. directories.global, cwd()) + eq(globalDir .. pathsep .. directories.global, cwd()) command('tabnext') - eq(globalDir .. '/' .. directories.tab, cwd()) - eq(globalDir .. '/' .. directories.tab, tcwd()) + eq(globalDir .. pathsep .. directories.tab, cwd()) + eq(globalDir .. pathsep .. directories.tab, tcwd()) eq(0, wlwd()) -- Still no window-local directory in this window -- Unless the global change happened in a tab with local directory @@ -220,9 +219,9 @@ for _, cmd in ipairs {'cd', 'chdir'} do -- But not in a window with its own local directory command('tabnext | wincmd w') - eq(globalDir .. '/' .. directories.window, cwd() ) + eq(globalDir .. pathsep .. directories.window, cwd() ) eq(0 , tlwd()) - eq(globalDir .. '/' .. directories.window, wcwd()) + eq(globalDir .. pathsep .. directories.window, wcwd()) end) end) end @@ -280,6 +279,9 @@ describe("getcwd()", function () end) it("returns empty string if working directory does not exist", function() + if helpers.iswin() then + return + end command("cd "..directories.global) command("call delete('../"..directories.global.."', 'd')") eq("", helpers.eval("getcwd()")) diff --git a/test/functional/ex_cmds/cmd_map_spec.lua b/test/functional/ex_cmds/cmd_map_spec.lua new file mode 100644 index 0000000000..77d025dcc7 --- /dev/null +++ b/test/functional/ex_cmds/cmd_map_spec.lua @@ -0,0 +1,772 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local feed_command = helpers.feed_command +local feed = helpers.feed +local eq = helpers.eq +local expect = helpers.expect +local eval = helpers.eval +local funcs = helpers.funcs +local insert = helpers.insert +local exc_exec = helpers.exc_exec +local Screen = require('test.functional.ui.screen') + +describe('mappings with <Cmd>', function() + local screen + local function cmdmap(lhs, rhs) + feed_command('noremap '..lhs..' <Cmd>'..rhs..'<cr>') + feed_command('noremap! '..lhs..' <Cmd>'..rhs..'<cr>') + end + + before_each(function() + clear() + screen = Screen.new(65, 8) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [4] = {bold = true}, + [5] = {background = Screen.colors.LightGrey}, + [6] = {foreground = Screen.colors.Blue1}, + [7] = {bold = true, reverse = true}, + }) + screen:attach() + + cmdmap('<F3>', 'let m = mode(1)') + cmdmap('<F4>', 'normal! ww') + cmdmap('<F5>', 'normal! "ay') + cmdmap('<F6>', 'throw "very error"') + feed_command([[ + function! TextObj() + if mode() !=# "v" + normal! v + end + call cursor(1,3) + normal! o + call cursor(2,4) + endfunction]]) + cmdmap('<F7>', 'call TextObj()') + insert([[ + some short lines + of test text]]) + feed('gg') + cmdmap('<F8>', 'startinsert') + cmdmap('<F9>', 'stopinsert') + feed_command("abbr foo <Cmd>let g:y = 17<cr>bar") + end) + + it('can be displayed', function() + feed_command('map <F3>') + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {6:<F3>} {6:*} {6:<Cmd>}let m = mode(1){6:<CR>} | + ]]) + end) + + it('handles invalid mappings', function() + feed_command('let x = 0') + feed_command('noremap <F3> <Cmd><Cmd>let x = 1<cr>') + feed('<F3>') + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:E5521: <Cmd> mapping must end with <CR> before second <Cmd>} | + ]]) + + feed_command('noremap <F3> <Cmd><F3>let x = 2<cr>') + feed('<F3>') + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:E5522: <Cmd> mapping must not include <F3> key} | + ]]) + + feed_command('noremap <F3> <Cmd>let x = 3') + feed('<F3>') + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:E5520: <Cmd> mapping must end with <CR>} | + ]]) + eq(0, eval('x')) + end) + + it('works in various modes and sees correct `mode()` value', function() + -- normal mode + feed('<F3>') + eq('n', eval('m')) + + -- visual mode + feed('v<F3>') + eq('v', eval('m')) + -- didn't leave visual mode + eq('v', eval('mode(1)')) + feed('<esc>') + eq('n', eval('mode(1)')) + + -- visual mapping in select mode + feed('gh<F3>') + eq('v', eval('m')) + -- didn't leave select mode + eq('s', eval('mode(1)')) + feed('<esc>') + eq('n', eval('mode(1)')) + + -- select mode mapping + feed_command('snoremap <F3> <Cmd>let m = mode(1)<cr>') + feed('gh<F3>') + eq('s', eval('m')) + -- didn't leave select mode + eq('s', eval('mode(1)')) + feed('<esc>') + eq('n', eval('mode(1)')) + + -- operator-pending mode + feed("d<F3>") + eq('no', eval('m')) + -- did leave operator-pending mode + eq('n', eval('mode(1)')) + + --insert mode + feed('i<F3>') + eq('i', eval('m')) + eq('i', eval('mode(1)')) + + -- replace mode + feed("<Ins><F3>") + eq('R', eval('m')) + eq('R', eval('mode(1)')) + feed('<esc>') + eq('n', eval('mode(1)')) + + -- virtual replace mode + feed("gR<F3>") + eq('Rv', eval('m')) + eq('Rv', eval('mode(1)')) + feed('<esc>') + eq('n', eval('mode(1)')) + + -- langmap works, but is not distinguished in mode(1) + feed(":set iminsert=1<cr>i<F3>") + eq('i', eval('m')) + eq('i', eval('mode(1)')) + feed('<esc>') + eq('n', eval('mode(1)')) + + feed(':<F3>') + eq('c', eval('m')) + eq('c', eval('mode(1)')) + feed('<esc>') + eq('n', eval('mode(1)')) + + -- terminal mode + feed_command('tnoremap <F3> <Cmd>let m = mode(1)<cr>') + feed_command('split | terminal') + feed('i') + eq('t', eval('mode(1)')) + feed('<F3>') + eq('t', eval('m')) + eq('t', eval('mode(1)')) + end) + + it('works in normal mode', function() + cmdmap('<F2>', 'let s = [mode(1), v:count, v:register]') + + -- check v:count and v:register works + feed('<F2>') + eq({'n', 0, '"'}, eval('s')) + feed('7<F2>') + eq({'n', 7, '"'}, eval('s')) + feed('"e<F2>') + eq({'n', 0, 'e'}, eval('s')) + feed('5"k<F2>') + eq({'n', 5, 'k'}, eval('s')) + feed('"+2<F2>') + eq({'n', 2, '+'}, eval('s')) + + -- text object enters visual mode + feed('<F7>') + screen:expect([[ + so{5:me short lines} | + {5:of }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- VISUAL --} | + ]]) + feed('<esc>') + + -- startinsert + feed('<F8>') + eq('i', eval('mode(1)')) + feed('<esc>') + + eq('n', eval('mode(1)')) + cmdmap(',a', 'call feedkeys("aalpha") \\| let g:a = getline(2)') + cmdmap(',b', 'call feedkeys("abeta", "x") \\| let g:b = getline(2)') + + feed(',a<F3>') + screen:expect([[ + some short lines | + of alpha^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT --} | + ]]) + -- feedkeys were not executed immediately + eq({'n', 'of test text'}, eval('[m,a]')) + eq('i', eval('mode(1)')) + feed('<esc>') + + feed(',b<F3>') + screen:expect([[ + some short lines | + of alphabet^atest text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + -- feedkeys(..., 'x') was executed immediately, but insert mode gets aborted + eq({'n', 'of alphabetatest text'}, eval('[m,b]')) + eq('n', eval('mode(1)')) + end) + + it('works in :normal command', function() + feed_command('noremap ,x <Cmd>call append(1, "xx")\\| call append(1, "aa")<cr>') + feed_command('noremap ,f <Cmd>nosuchcommand<cr>') + feed_command('noremap ,e <Cmd>throw "very error"\\| call append(1, "yy")<cr>') + feed_command('noremap ,m <Cmd>echoerr "The message."\\| call append(1, "zz")<cr>') + feed_command('noremap ,w <Cmd>for i in range(5)\\|if i==1\\|echoerr "Err"\\|endif\\|call append(1, i)\\|endfor<cr>') + + feed(":normal ,x<cr>") + screen:expect([[ + ^some short lines | + aa | + xx | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + + eq('Vim:E492: Not an editor command: nosuchcommand', exc_exec("normal ,f")) + eq('very error', exc_exec("normal ,e")) + eq('Vim(echoerr):The message.', exc_exec("normal ,m")) + feed('w') + screen:expect([[ + some ^short lines | + aa | + xx | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + + feed_command(':%d') + eq('Vim(echoerr):Err', exc_exec("normal ,w")) + screen:expect([[ + ^ | + 0 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + --No lines in buffer-- | + ]]) + + feed_command(':%d') + feed_command(':normal ,w') + screen:expect([[ + ^ | + 4 | + 3 | + 2 | + 1 | + 0 | + {1:~ }| + {2:Err} | + ]]) + end) + + it('works in visual mode', function() + -- can extend visual mode + feed('v<F4>') + screen:expect([[ + {5:some short }^lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- VISUAL --} | + ]]) + eq('v', funcs.mode(1)) + + -- can invoke operator, ending visual mode + feed('<F5>') + eq('n', funcs.mode(1)) + eq({'some short l'}, funcs.getreg('a',1,1)) + + -- error doesn't interrupt visual mode + feed('ggvw<F6>') + screen:expect([[ + {5:some }short lines | + of test text | + {1:~ }| + {1:~ }| + {7: }| + {2:Error detected while processing :} | + {2:E605: Exception not caught: very error} | + {3:Press ENTER or type command to continue}^ | + ]]) + feed('<cr>') + eq('E605: Exception not caught: very error', eval('v:errmsg')) + -- still in visual mode, <cr> was consumed by the error prompt + screen:expect([[ + {5:some }^short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- VISUAL --} | + ]]) + eq('v', funcs.mode(1)) + feed('<F7>') + screen:expect([[ + so{5:me short lines} | + {5:of }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- VISUAL --} | + ]]) + eq('v', funcs.mode(1)) + + -- startinsert gives "-- (insert) VISUAL --" mode + feed('<F8>') + screen:expect([[ + so{5:me short lines} | + {5:of }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- (insert) VISUAL --} | + ]]) + eq('v', eval('mode(1)')) + feed('<esc>') + eq('i', eval('mode(1)')) + end) + + it('works in select mode', function() + feed_command('snoremap <F1> <cmd>throw "very error"<cr>') + feed_command('snoremap <F2> <cmd>normal! <c-g>"by<cr>') + -- can extend select mode + feed('gh<F4>') + screen:expect([[ + {5:some short }^lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- SELECT --} | + ]]) + eq('s', funcs.mode(1)) + + -- visual mapping in select mode restart selct mode after operator + feed('<F5>') + eq('s', funcs.mode(1)) + eq({'some short l'}, funcs.getreg('a',1,1)) + + -- select mode mapping works, and does not restart select mode + feed('<F2>') + eq('n', funcs.mode(1)) + eq({'some short l'}, funcs.getreg('b',1,1)) + + -- error doesn't interrupt temporary visual mode + feed('<esc>ggvw<c-g><F6>') + screen:expect([[ + {5:some }short lines | + of test text | + {1:~ }| + {1:~ }| + {7: }| + {2:Error detected while processing :} | + {2:E605: Exception not caught: very error} | + {3:Press ENTER or type command to continue}^ | + ]]) + feed('<cr>') + eq('E605: Exception not caught: very error', eval('v:errmsg')) + -- still in visual mode, <cr> was consumed by the error prompt + screen:expect([[ + {5:some }^short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- VISUAL --} | + ]]) + -- quirk: restoration of select mode is not performed + eq('v', funcs.mode(1)) + + -- error doesn't interrupt select mode + feed('<esc>ggvw<c-g><F1>') + screen:expect([[ + {5:some }short lines | + of test text | + {1:~ }| + {1:~ }| + {7: }| + {2:Error detected while processing :} | + {2:E605: Exception not caught: very error} | + {3:Press ENTER or type command to continue}^ | + ]]) + feed('<cr>') + eq('E605: Exception not caught: very error', eval('v:errmsg')) + -- still in select mode, <cr> was consumed by the error prompt + screen:expect([[ + {5:some }^short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- SELECT --} | + ]]) + -- quirk: restoration of select mode is not performed + eq('s', funcs.mode(1)) + + feed('<F7>') + screen:expect([[ + so{5:me short lines} | + {5:of }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- SELECT --} | + ]]) + eq('s', funcs.mode(1)) + + -- startinsert gives "-- SELECT (insert) --" mode + feed('<F8>') + screen:expect([[ + so{5:me short lines} | + {5:of }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- (insert) SELECT --} | + ]]) + eq('s', eval('mode(1)')) + feed('<esc>') + eq('i', eval('mode(1)')) + end) + + + it('works in operator-pending mode', function() + feed('d<F4>') + expect([[ + lines + of test text]]) + eq({'some short '}, funcs.getreg('"',1,1)) + feed('.') + expect([[ + test text]]) + eq({'lines', 'of '}, funcs.getreg('"',1,1)) + feed('uu') + expect([[ + some short lines + of test text]]) + + -- error aborts operator-pending, operator not performed + feed('d<F6>') + screen:expect([[ + some short lines | + of test text | + {1:~ }| + {1:~ }| + {7: }| + {2:Error detected while processing :} | + {2:E605: Exception not caught: very error} | + {3:Press ENTER or type command to continue}^ | + ]]) + feed('<cr>') + eq('E605: Exception not caught: very error', eval('v:errmsg')) + expect([[ + some short lines + of test text]]) + + feed('"bd<F7>') + expect([[ + soest text]]) + eq(funcs.getreg('b',1,1), {'me short lines', 'of t'}) + + -- startinsert aborts operator + feed('d<F8>') + eq('i', eval('mode(1)')) + expect([[ + soest text]]) + end) + + it('works in insert mode', function() + + -- works the same as <c-o>w<c-o>w + feed('iindeed <F4>little ') + screen:expect([[ + indeed some short little ^lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT --} | + ]]) + + feed('<F6>') + screen:expect([[ + indeed some short little lines | + of test text | + {1:~ }| + {1:~ }| + {7: }| + {2:Error detected while processing :} | + {2:E605: Exception not caught: very error} | + {3:Press ENTER or type command to continue}^ | + ]]) + + + feed('<cr>') + eq('E605: Exception not caught: very error', eval('v:errmsg')) + -- still in insert + screen:expect([[ + indeed some short little ^lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT --} | + ]]) + eq('i', eval('mode(1)')) + + -- When entering visual mode from InsertEnter autocmd, an async event, or + -- a <cmd> mapping, vim ends up in undocumented "INSERT VISUAL" mode. If a + -- vim patch decides to disable this mode, this test is expected to fail. + feed('<F7>stuff ') + screen:expect([[ + in{5:deed some short little lines} | + {5:of stuff }^test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT VISUAL --} | + ]]) + expect([[ + indeed some short little lines + of stuff test text]]) + + feed('<F5>') + eq(funcs.getreg('a',1,1), {'deed some short little lines', 'of stuff t'}) + + -- still in insert + screen:expect([[ + in^deed some short little lines | + of stuff test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT --} | + ]]) + eq('i', eval('mode(1)')) + + -- also works as part of abbreviation + feed('<space>foo ') + screen:expect([[ + in bar ^deed some short little lines | + of stuff test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT --} | + ]]) + eq(17, eval('g:y')) + + -- :startinsert does nothing + feed('<F8>') + eq('i', eval('mode(1)')) + + -- :stopinsert works + feed('<F9>') + eq('n', eval('mode(1)')) + end) + + it('works in cmdline mode', function() + cmdmap('<F2>', 'call setcmdpos(2)') + feed(':text<F3>') + eq('c', eval('m')) + -- didn't leave cmdline mode + eq('c', eval('mode(1)')) + feed('<cr>') + eq('n', eval('mode(1)')) + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:E492: Not an editor command: text} | + ]]) + + feed(':echo 2<F6>') + screen:expect([[ + some short lines | + of test text | + {1:~ }| + {7: }| + :echo 2 | + {2:Error detected while processing :} | + {2:E605: Exception not caught: very error} | + :echo 2^ | + ]]) + eq('E605: Exception not caught: very error', eval('v:errmsg')) + -- didn't leave cmdline mode + eq('c', eval('mode(1)')) + feed('+2<cr>') + screen:expect([[ + some short lines | + of test text | + {7: }| + :echo 2 | + {2:Error detected while processing :} | + {2:E605: Exception not caught: very error} | + 4 | + {3:Press ENTER or type command to continue}^ | + ]]) + -- however, message scrolling may cause extra CR prompt + -- This is consistent with output from async events. + feed('<cr>') + screen:expect([[ + ^some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + eq('n', eval('mode(1)')) + + feed(':let g:x = 3<F4>') + screen:expect([[ + some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :let g:x = 3^ | + ]]) + feed('+2<cr>') + -- cursor was moved in the background + screen:expect([[ + some short ^lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :let g:x = 3+2 | + ]]) + eq(5, eval('g:x')) + + feed(':let g:y = 7<F8>') + screen:expect([[ + some short lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :let g:y = 7^ | + ]]) + eq('c', eval('mode(1)')) + feed('+2<cr>') + -- startinsert takes effect after leaving cmdline mode + screen:expect([[ + some short ^lines | + of test text | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:-- INSERT --} | + ]]) + eq('i', eval('mode(1)')) + eq(9, eval('g:y')) + + end) + +end) + diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index e3b4a1c504..3d550588e7 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source -local eq, next_msg = helpers.eq, helpers.next_message +local eq, next_msg = helpers.eq, helpers.next_msg local exc_exec = helpers.exc_exec local command = helpers.command local eval = helpers.eval diff --git a/test/functional/ex_cmds/drop_spec.lua b/test/functional/ex_cmds/drop_spec.lua index 9105b84367..30dbd27d37 100644 --- a/test/functional/ex_cmds/drop_spec.lua +++ b/test/functional/ex_cmds/drop_spec.lua @@ -44,14 +44,14 @@ describe(":drop", function() feed_command("edit tmp2") feed_command("drop tmp1") screen:expect([[ - {2:|}^ | - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| + {2:│}^ | + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| {2:tmp2 }{1:tmp1 }| :drop tmp1 | ]]) @@ -64,14 +64,14 @@ describe(":drop", function() feed("iABC<esc>") feed_command("drop tmp3") screen:expect([[ - ^ {2:|} | - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {1:tmp3 }{2:|}{0:~ }| - ABC {2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }| + ^ {2:│} | + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {1:tmp3 }{2:│}{0:~ }| + ABC {2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }| {2:tmp2 [+] tmp1 }| "tmp3" [New File] | ]]) diff --git a/test/functional/ex_cmds/map_spec.lua b/test/functional/ex_cmds/map_spec.lua new file mode 100644 index 0000000000..84d5bc2335 --- /dev/null +++ b/test/functional/ex_cmds/map_spec.lua @@ -0,0 +1,28 @@ +local helpers = require("test.functional.helpers")(after_each) + +local eq = helpers.eq +local feed = helpers.feed +local meths = helpers.meths +local clear = helpers.clear +local command = helpers.command +local expect = helpers.expect + +describe(':*map', function() + before_each(clear) + + it('are not affected by &isident', function() + meths.set_var('counter', 0) + command('nnoremap <C-x> :let counter+=1<CR>') + meths.set_option('isident', ('%u'):format(('>'):byte())) + command('nnoremap <C-y> :let counter+=1<CR>') + -- &isident used to disable keycode parsing here as well + feed('\24\25<C-x><C-y>') + eq(4, meths.get_var('counter')) + end) + + it(':imap <M-">', function() + command('imap <M-"> foo') + feed('i-<M-">-') + expect('-foo-') + end) +end) diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua index 5d658f10bb..a5b327095e 100644 --- a/test/functional/ex_cmds/mksession_spec.lua +++ b/test/functional/ex_cmds/mksession_spec.lua @@ -6,6 +6,7 @@ local command = helpers.command local get_pathsep = helpers.get_pathsep local eq = helpers.eq local funcs = helpers.funcs +local rmdir = helpers.rmdir local file_prefix = 'Xtest-functional-ex_cmds-mksession_spec' @@ -20,7 +21,7 @@ describe(':mksession', function() after_each(function() os.remove(session_file) - lfs.rmdir(tab_dir) + rmdir(tab_dir) end) it('restores tab-local working directories', function() diff --git a/test/functional/ex_cmds/mkview_spec.lua b/test/functional/ex_cmds/mkview_spec.lua index 97a49dbbd5..fef8065b2e 100644 --- a/test/functional/ex_cmds/mkview_spec.lua +++ b/test/functional/ex_cmds/mkview_spec.lua @@ -24,7 +24,7 @@ describe(':mkview', function() after_each(function() -- Remove any views created in the view directory rmdir(view_dir) - lfs.rmdir(local_dir) + rmdir(local_dir) end) it('viewoption curdir restores local current directory', function() diff --git a/test/functional/ex_cmds/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua index 4002855c24..448326cdfb 100644 --- a/test/functional/ex_cmds/oldfiles_spec.lua +++ b/test/functional/ex_cmds/oldfiles_spec.lua @@ -29,6 +29,7 @@ describe(':oldfiles', function() it('shows most recently used files', function() local screen = Screen.new(100, 5) screen:attach() + feed_command("set display-=msgsep") feed_command('edit testfile1') feed_command('edit testfile2') feed_command('wshada') diff --git a/test/functional/ex_cmds/sign_spec.lua b/test/functional/ex_cmds/sign_spec.lua index b37e6e8563..df0f5db860 100644 --- a/test/functional/ex_cmds/sign_spec.lua +++ b/test/functional/ex_cmds/sign_spec.lua @@ -16,8 +16,8 @@ describe('sign', function() nvim('command', 'sign place 34 line=3 name=Foo buffer='..buf2) -- now unplace without specifying a buffer nvim('command', 'sign unplace 34') - eq("\n--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf1)) - eq("\n--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf2)) + eq("--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf1)) + eq("--- Signs ---\n", nvim('command_output', 'sign place buffer='..buf2)) end) end) end) diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua index 863d439080..bcf83698bb 100644 --- a/test/functional/ex_cmds/write_spec.lua +++ b/test/functional/ex_cmds/write_spec.lua @@ -10,8 +10,6 @@ local feed_command = helpers.feed_command local funcs = helpers.funcs local meths = helpers.meths -if helpers.pending_win32(pending) then return end - local fname = 'Xtest-functional-ex_cmds-write' local fname_bak = fname .. '~' local fname_broken = fname_bak .. 'broken' @@ -36,7 +34,11 @@ describe(':write', function() it('&backupcopy=auto preserves symlinks', function() command('set backupcopy=auto') write_file('test_bkc_file.txt', 'content0') - command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + if helpers.iswin() then + command("silent !mklink test_bkc_link.txt test_bkc_file.txt") + else + command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + end source([[ edit test_bkc_link.txt call setline(1, ['content1']) @@ -49,7 +51,11 @@ describe(':write', function() it('&backupcopy=no replaces symlink with new file', function() command('set backupcopy=no') write_file('test_bkc_file.txt', 'content0') - command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + if helpers.iswin() then + command("silent !mklink test_bkc_link.txt test_bkc_file.txt") + else + command("silent !ln -s test_bkc_file.txt test_bkc_link.txt") + end source([[ edit test_bkc_link.txt call setline(1, ['content1']) @@ -82,8 +88,10 @@ describe(':write', function() command('let $HOME=""') eq(funcs.fnamemodify('.', ':p:h'), funcs.fnamemodify('.', ':p:h:~')) -- Message from check_overwrite - eq(('\nE17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'), - redir_exec('write .')) + if not helpers.iswin() then + eq(('\nE17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'), + redir_exec('write .')) + end meths.set_option('writeany', true) -- Message from buf_write eq(('\nE502: "." is a directory'), @@ -100,9 +108,16 @@ describe(':write', function() funcs.setfperm(fname, 'r--------') eq('Vim(write):E505: "Xtest-functional-ex_cmds-write" is read-only (add ! to override)', exc_exec('write')) - os.remove(fname) - os.remove(fname_bak) + if helpers.iswin() then + eq(0, os.execute('del /q/f ' .. fname)) + eq(0, os.execute('rd /q/s ' .. fname_bak)) + else + eq(true, os.remove(fname)) + eq(true, os.remove(fname_bak)) + end write_file(fname_bak, 'TTYX') + -- FIXME: exc_exec('write!') outputs 0 in Windows + if helpers.iswin() then return end lfs.link(fname_bak .. ('/xxxxx'):rep(20), fname, true) eq('Vim(write):E166: Can\'t open linked file for writing', exc_exec('write!')) diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c index 8dbec2aaee..a744d5df46 100644 --- a/test/functional/fixtures/shell-test.c +++ b/test/functional/fixtures/shell-test.c @@ -4,6 +4,18 @@ #include <stdio.h> #include <string.h> #include <stdint.h> +#ifdef _MSC_VER +#include <Windows.h> +#define usleep(usecs) Sleep(usecs/1000) +#else +#include <unistd.h> +#endif + +static void wait(void) +{ + fflush(stdout); + usleep(10*1000); +} static void help(void) { @@ -61,6 +73,22 @@ int main(int argc, char **argv) for (uint8_t i = 0; i < number; i++) { printf("%d: %s\n", (int) i, argv[3]); } + } else if (strcmp(argv[1], "UTF-8") == 0) { + // test split-up UTF-8 sequence + printf("\xc3"); wait(); + printf("\xa5\n"); wait(); + + // split up a 2+2 grapheme clusters all possible ways + printf("ref: \xc3\xa5\xcc\xb2\n"); wait(); + + printf("1: \xc3"); wait(); + printf("\xa5\xcc\xb2\n"); wait(); + + printf("2: \xc3\xa5"); wait(); + printf("\xcc\xb2\n"); wait(); + + printf("3: \xc3\xa5\xcc"); wait(); + printf("\xb2\n"); wait(); } else { fprintf(stderr, "Unknown first argument\n"); return 3; diff --git a/test/functional/fixtures/shell_data.txt b/test/functional/fixtures/shell_data.txt Binary files differnew file mode 100644 index 0000000000..ef3506c5b1 --- /dev/null +++ b/test/functional/fixtures/shell_data.txt diff --git a/test/functional/fixtures/tty-test.c b/test/functional/fixtures/tty-test.c index edcbe23f86..4f0858acdb 100644 --- a/test/functional/fixtures/tty-test.c +++ b/test/functional/fixtures/tty-test.c @@ -41,6 +41,7 @@ static void walk_cb(uv_handle_t *handle, void *arg) } } +#ifndef WIN32 static void sig_handler(int signum) { switch (signum) { @@ -57,6 +58,7 @@ static void sig_handler(int signum) return; } } +#endif #ifdef WIN32 static void sigwinch_cb(uv_signal_t *handle, int signum) @@ -94,7 +96,14 @@ static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf) uv_tty_init(&write_loop, &out, fileno(stdout), 0); uv_write_t req; - uv_buf_t b = {.base = buf->base, .len = (size_t)cnt}; + uv_buf_t b = { + .base = buf->base, +#ifdef WIN32 + .len = (ULONG)cnt +#else + .len = (size_t)cnt +#endif + }; uv_write(&req, STRUCT_CAST(uv_stream_t, &out), &b, 1, NULL); uv_run(&write_loop, UV_RUN_DEFAULT); diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index da334d4ac6..bf11042dd6 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -14,10 +14,13 @@ local check_cores = global_helpers.check_cores local check_logs = global_helpers.check_logs local neq = global_helpers.neq local eq = global_helpers.eq +local expect_err = global_helpers.expect_err local ok = global_helpers.ok local map = global_helpers.map +local matches = global_helpers.matches local filter = global_helpers.filter local dedent = global_helpers.dedent +local table_flatten = global_helpers.table_flatten local start_dir = lfs.currentdir() -- XXX: NVIM_PROG takes precedence, QuickBuild sets it. @@ -96,14 +99,14 @@ local function request(method, ...) return rv end -local function next_message() - return session:next_message() +local function next_msg(timeout) + return session:next_message(timeout and timeout or 10000) end local function expect_twostreams(msgs1, msgs2) local pos1, pos2 = 1, 1 while pos1 <= #msgs1 or pos2 <= #msgs2 do - local msg = next_message() + local msg = next_msg() if pos1 <= #msgs1 and pcall(eq, msgs1[pos1], msg) then pos1 = pos1 + 1 elseif pos2 <= #msgs2 then @@ -116,6 +119,46 @@ local function expect_twostreams(msgs1, msgs2) end end +-- Expects a sequence of next_msg() results. If multiple sequences are +-- passed they are tried until one succeeds, in order of shortest to longest. +local function expect_msg_seq(...) + if select('#', ...) < 1 then + error('need at least 1 argument') + end + local seqs = {...} + table.sort(seqs, function(a, b) -- Sort ascending, by (shallow) length. + return #a < #b + end) + + local actual_seq = {} + local final_error = '' + local function cat_err(err1, err2) + if err1 == nil then + return err2 + end + return string.format('%s\n%s\n%s', err1, string.rep('=', 78), err2) + end + for anum = 1, #seqs do + local expected_seq = seqs[anum] + -- Collect enough messages to compare the next expected sequence. + while #actual_seq < #expected_seq do + local msg = next_msg(10000) -- Big timeout for ASAN/valgrind. + if msg == nil then + error(cat_err(final_error, + string.format('got %d messages, expected %d', + #actual_seq, #expected_seq))) + end + table.insert(actual_seq, msg) + end + local status, result = pcall(eq, expected_seq, actual_seq) + if status then + return result + end + final_error = cat_err(final_error, result) + end + error(final_error) +end + local function call_and_stop_on_error(...) local status, result = copcall(...) -- luacheck: ignore if not status then @@ -261,6 +304,7 @@ local function retry(max, max_ms, fn) if status then return result end + luv.update_time() -- Update cached value of luv.now() (libuv: uv_now()). if (max and tries >= max) or (luv.now() - start_time > timeout) then if type(result) == "string" then result = "\nretry() attempts: "..tostring(tries).."\n"..result @@ -333,8 +377,8 @@ local function feed_command(...) end -- Dedent the given text and write it to the file name. -local function write_file(name, text, dont_dedent) - local file = io.open(name, 'w') +local function write_file(name, text, no_dedent, append) + local file = io.open(name, (append and 'a' or 'w')) if type(text) == 'table' then -- Byte blob local bytes = text @@ -342,7 +386,7 @@ local function write_file(name, text, dont_dedent) for _, char in ipairs(bytes) do text = ('%s%c'):format(text, char) end - elseif not dont_dedent then + elseif not no_dedent then text = dedent(text) end file:write(text) @@ -382,9 +426,8 @@ end local function set_shell_powershell() source([[ - set shell=powershell shellquote=\" shellpipe=\| shellredir=> - set shellcmdflag=\ -NoLogo\ -NoProfile\ -ExecutionPolicy\ RemoteSigned\ -Command - let &shellxquote=' ' + set shell=powershell shellquote=( shellpipe=\| shellredir=> shellxquote= + set shellcmdflag=-NoLogo\ -NoProfile\ -ExecutionPolicy\ RemoteSigned\ -Command ]]) end @@ -600,7 +643,12 @@ local function redir_exec(cmd) end local function get_pathsep() - return funcs.fnamemodify('.', ':p'):sub(-1) + return iswin() and '\\' or '/' +end + +local function pathroot() + local pathsep = package.config:sub(1,1) + return iswin() and (nvim_dir:sub(1,2)..pathsep) or '/' end -- Returns a valid, platform-independent $NVIM_LISTEN_ADDRESS. @@ -613,7 +661,7 @@ local function new_pipename() end local function missing_provider(provider) - if provider == 'ruby' then + if provider == 'ruby' or provider == 'node' then local prog = funcs['provider#' .. provider .. '#Detect']() return prog == '' and (provider .. ' not detected') or false elseif provider == 'python' or provider == 'python3' then @@ -644,7 +692,7 @@ local function alter_slashes(obj) end local function hexdump(str) - local len = string.len( str ) + local len = string.len(str) local dump = "" local hex = "" local asc = "" @@ -652,96 +700,99 @@ local function hexdump(str) for i = 1, len do if 1 == i % 8 then dump = dump .. hex .. asc .. "\n" - hex = string.format( "%04x: ", i - 1 ) + hex = string.format("%04x: ", i - 1) asc = "" end - local ord = string.byte( str, i ) - hex = hex .. string.format( "%02x ", ord ) + local ord = string.byte(str, i) + hex = hex .. string.format("%02x ", ord) if ord >= 32 and ord <= 126 then - asc = asc .. string.char( ord ) + asc = asc .. string.char(ord) else asc = asc .. "." end end - return dump .. hex - .. string.rep( " ", 8 - len % 8 ) .. asc - + return dump .. hex .. string.rep(" ", 8 - len % 8) .. asc end local module = { - prepend_argv = prepend_argv, + NIL = mpack.NIL, + alter_slashes = alter_slashes, + buffer = buffer, + bufmeths = bufmeths, + call = nvim_call, clear = clear, + command = nvim_command, connect = connect, - retry = retry, - spawn = spawn, + curbuf = curbuf, + curbuf_contents = curbuf_contents, + curbufmeths = curbufmeths, + curtab = curtab, + curtabmeths = curtabmeths, + curwin = curwin, + curwinmeths = curwinmeths, dedent = dedent, - source = source, - rawfeed = rawfeed, - insert = insert, - iswin = iswin, - feed = feed, - feed_command = feed_command, - eval = nvim_eval, - call = nvim_call, - command = nvim_command, - request = request, - next_message = next_message, - expect_twostreams = expect_twostreams, - run = run, - stop = stop, eq = eq, - neq = neq, + eval = nvim_eval, + exc_exec = exc_exec, expect = expect, expect_any = expect_any, - ok = ok, - map = map, + expect_err = expect_err, + expect_msg_seq = expect_msg_seq, + expect_twostreams = expect_twostreams, + feed = feed, + feed_command = feed_command, filter = filter, + funcs = funcs, + get_pathsep = get_pathsep, + hexdump = hexdump, + insert = insert, + iswin = iswin, + map = map, + matches = matches, + merge_args = merge_args, + meth_pcall = meth_pcall, + meths = meths, + missing_provider = missing_provider, + mkdir = lfs.mkdir, + neq = neq, + new_pipename = new_pipename, + next_msg = next_msg, nvim = nvim, + nvim_argv = nvim_argv, nvim_async = nvim_async, + nvim_dir = nvim_dir, nvim_prog = nvim_prog, - nvim_argv = nvim_argv, nvim_set = nvim_set, - nvim_dir = nvim_dir, - buffer = buffer, - window = window, - tabpage = tabpage, - curbuf = curbuf, - curwin = curwin, - curtab = curtab, - curbuf_contents = curbuf_contents, - wait = wait, - sleep = sleep, - set_session = set_session, - write_file = write_file, - read_file = read_file, + ok = ok, os_name = os_name, - rmdir = rmdir, - mkdir = lfs.mkdir, - exc_exec = exc_exec, - redir_exec = redir_exec, - merge_args = merge_args, - funcs = funcs, - meths = meths, - bufmeths = bufmeths, - winmeths = winmeths, - tabmeths = tabmeths, - uimeths = uimeths, - curbufmeths = curbufmeths, - curwinmeths = curwinmeths, - curtabmeths = curtabmeths, + pathroot = pathroot, pending_win32 = pending_win32, - skip_fragile = skip_fragile, + prepend_argv = prepend_argv, + rawfeed = rawfeed, + read_file = read_file, + redir_exec = redir_exec, + request = request, + retry = retry, + rmdir = rmdir, + run = run, + set_session = set_session, set_shell_powershell = set_shell_powershell, + skip_fragile = skip_fragile, + sleep = sleep, + source = source, + spawn = spawn, + stop = stop, + table_flatten = table_flatten, + tabmeths = tabmeths, + tabpage = tabpage, tmpname = tmpname, - meth_pcall = meth_pcall, - NIL = mpack.NIL, - get_pathsep = get_pathsep, - missing_provider = missing_provider, - alter_slashes = alter_slashes, - hexdump = hexdump, - new_pipename = new_pipename, + uimeths = uimeths, + wait = wait, + window = window, + winmeths = winmeths, + write_file = write_file, } return function(after_each) diff --git a/test/functional/insert/insert_spec.lua b/test/functional/insert/insert_spec.lua new file mode 100644 index 0000000000..427954f5a6 --- /dev/null +++ b/test/functional/insert/insert_spec.lua @@ -0,0 +1,41 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local command = helpers.command +local eq = helpers.eq +local expect = helpers.expect +local funcs = helpers.funcs + +describe('insert-mode', function() + before_each(function() + clear() + end) + + it('CTRL-@', function() + -- Inserts last-inserted text, leaves insert-mode. + insert('hello') + feed('i<C-@>x') + expect('hellhello') + + -- C-Space is the same as C-@. + -- CTRL-SPC inserts last-inserted text, leaves insert-mode. + feed('i<C-Space>x') + expect('hellhellhello') + + -- CTRL-A inserts last inserted text + feed('i<C-A>x') + expect('hellhellhellhelloxo') + end) + + it('ALT/META #8213', function() + -- Mapped ALT-chord behaves as mapped. + command('inoremap <M-l> meta-l') + command('inoremap <A-j> alt-j') + feed('i<M-l> xxx <A-j><M-h>a<A-h>') + expect('meta-l xxx alt-j') + eq({ 0, 1, 14, 0, }, funcs.getpos('.')) + -- Unmapped ALT-chord behaves as ESC+c. + command('iunmap <M-l>') + feed('0i<M-l>') + eq({ 0, 1, 2, 0, }, funcs.getpos('.')) + end) +end) diff --git a/test/functional/insert/last_inserted_spec.lua b/test/functional/insert/last_inserted_spec.lua deleted file mode 100644 index dce23a3790..0000000000 --- a/test/functional/insert/last_inserted_spec.lua +++ /dev/null @@ -1,22 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local expect = helpers.expect - -clear() - -describe('insert-mode', function() - it('CTRL-@ inserts last-inserted text, leaves insert-mode', function() - insert('hello') - feed('i<C-@>x') - expect('hellhello') - end) - -- C-Space is the same as C-@ - it('CTRL-SPC inserts last-inserted text, leaves insert-mode', function() - feed('i<C-Space>x') - expect('hellhellhello') - end) - it('CTRL-A inserts last inserted text', function() - feed('i<C-A>x') - expect('hellhellhellhelloxo') - end) -end) diff --git a/test/functional/legacy/003_cindent_spec.lua b/test/functional/legacy/003_cindent_spec.lua index 58e87354fb..1cede8a7d7 100644 --- a/test/functional/legacy/003_cindent_spec.lua +++ b/test/functional/legacy/003_cindent_spec.lua @@ -1,4 +1,5 @@ -- Test for 'cindent'. +-- For new tests, consider putting them in test_cindent.vim. -- -- There are 50+ test command blocks (the stuff between STARTTEST and ENDTEST) -- in the original test. These have been converted to "it" test cases here. @@ -1956,7 +1957,8 @@ describe('cindent', function() } ]=]) - feed_command('set tw=0 wm=60 columns=80 noai fo=croq') + feed_command('set tw=0 noai fo=croq') + feed_command('let &wm = &columns - 20') feed_command('/serious/e') feed('a about life, the universe, and the rest<esc>') diff --git a/test/functional/legacy/008_autocommands_spec.lua b/test/functional/legacy/008_autocommands_spec.lua index 7474f1e068..453638ce45 100644 --- a/test/functional/legacy/008_autocommands_spec.lua +++ b/test/functional/legacy/008_autocommands_spec.lua @@ -5,9 +5,10 @@ local helpers = require('test.functional.helpers')(after_each) local feed, source = helpers.feed, helpers.source local clear, feed_command, expect, eq, eval = helpers.clear, helpers.feed_command, helpers.expect, helpers.eq, helpers.eval local write_file, wait, dedent = helpers.write_file, helpers.wait, helpers.dedent -local io = require('io') +local read_file = helpers.read_file describe('autocommands that delete and unload buffers:', function() + local test_file = 'Xtest-008_autocommands.out' local text1 = dedent([[ start of Xxx1 test @@ -18,7 +19,7 @@ describe('autocommands that delete and unload buffers:', function() write_file('Xxx2', text2..'\n') end) teardown(function() - os.remove('test.out') + os.remove(test_file) os.remove('Xxx1') os.remove('Xxx2') end) @@ -65,7 +66,8 @@ describe('autocommands that delete and unload buffers:', function() endwhile endfunc func WriteToOut() - edit! test.out + edit! ]]..test_file..[[ + $put ='VimLeave done' write endfunc @@ -86,6 +88,6 @@ describe('autocommands that delete and unload buffers:', function() feed_command('q') wait() eq('VimLeave done', - string.match(io.open('test.out', 'r'):read('*all'), "^%s*(.-)%s*$")) + string.match(read_file(test_file), "^%s*(.-)%s*$")) end) end) diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua index d969a8bd37..c2667d28d2 100644 --- a/test/functional/legacy/011_autocommands_spec.lua +++ b/test/functional/legacy/011_autocommands_spec.lua @@ -18,10 +18,9 @@ local clear, feed_command, expect, eq, neq, dedent, write_file, feed = helpers.clear, helpers.feed_command, helpers.expect, helpers.eq, helpers.neq, helpers.dedent, helpers.write_file, helpers.feed -if helpers.pending_win32(pending) then return end - local function has_gzip() - return os.execute('gzip --help >/dev/null 2>&1') == 0 + local null = helpers.iswin() and 'nul' or '/dev/null' + return os.execute('gzip --help >' .. null .. ' 2>&1') == 0 end local function prepare_gz_file(name, text) @@ -142,6 +141,7 @@ describe('file reading, writing and bufnew and filter autocommands', function() end) it('FilterReadPre, FilterReadPost', function() + if helpers.pending_win32(pending) then return end -- Write a special input file for this test block. write_file('test.out', dedent([[ startstart diff --git a/test/functional/legacy/025_jump_tag_hidden_spec.lua b/test/functional/legacy/025_jump_tag_hidden_spec.lua index 0d51b4da26..dd89a3680e 100644 --- a/test/functional/legacy/025_jump_tag_hidden_spec.lua +++ b/test/functional/legacy/025_jump_tag_hidden_spec.lua @@ -5,8 +5,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local feed_command, expect = helpers.feed_command, helpers.expect -if helpers.pending_win32(pending) then return end - describe('jump to a tag with hidden set', function() setup(clear) @@ -25,12 +23,17 @@ describe('jump to a tag with hidden set', function() feed_command('set hidden') -- Create a link from test25.dir to the current directory. - feed_command('!rm -f test25.dir') - feed_command('!ln -s . test25.dir') + if helpers.iswin() then + feed_command('!rd /q/s test25.dir') + feed_command('!mklink /j test25.dir .') + else + feed_command('!rm -f test25.dir') + feed_command('!ln -s . test25.dir') + end -- Create tags.text, with the current directory name inserted. feed_command('/tags line') - feed_command('r !pwd') + feed_command('r !' .. (helpers.iswin() and 'cd' or 'pwd')) feed('d$/test<cr>') feed('hP:.w! tags.test<cr>') @@ -39,7 +42,13 @@ describe('jump to a tag with hidden set', function() -- space will then be eaten by hit-return, instead of moving the cursor to 'd'. feed_command('set tags=tags.test') feed('G<C-]> x:yank a<cr>') - feed_command('!rm -f Xxx test25.dir tags.test') + feed_command("call delete('tags.test')") + feed_command("call delete('Xxx')") + if helpers.iswin() then + feed_command('!rd /q test25.dir') + else + feed_command('!rm -f test25.dir') + end -- Put @a and remove empty line feed_command('%d') diff --git a/test/functional/legacy/030_fileformats_spec.lua b/test/functional/legacy/030_fileformats_spec.lua index 7384fdf847..2fd51602d8 100644 --- a/test/functional/legacy/030_fileformats_spec.lua +++ b/test/functional/legacy/030_fileformats_spec.lua @@ -5,8 +5,6 @@ local feed, clear, command = helpers.feed, helpers.clear, helpers.command local eq, write_file = helpers.eq, helpers.write_file local wait = helpers.wait -if helpers.pending_win32(pending) then return end - describe('fileformats option', function() setup(function() clear() diff --git a/test/functional/legacy/051_highlight_spec.lua b/test/functional/legacy/051_highlight_spec.lua index 2ef74196ee..40f70de2ec 100644 --- a/test/functional/legacy/051_highlight_spec.lua +++ b/test/functional/legacy/051_highlight_spec.lua @@ -8,8 +8,6 @@ local eq = helpers.eq local wait = helpers.wait local exc_exec = helpers.exc_exec -if helpers.pending_win32(pending) then return end - describe(':highlight', function() setup(clear) diff --git a/test/functional/legacy/059_utf8_spell_checking_spec.lua b/test/functional/legacy/059_utf8_spell_checking_spec.lua index 120e469ab2..8630ac58ef 100644 --- a/test/functional/legacy/059_utf8_spell_checking_spec.lua +++ b/test/functional/legacy/059_utf8_spell_checking_spec.lua @@ -5,8 +5,6 @@ local feed, insert, source = helpers.feed, helpers.insert, helpers.source local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect local write_file, call = helpers.write_file, helpers.call -if helpers.pending_win32(pending) then return end - local function write_latin1(name, text) text = call('iconv', text, 'utf-8', 'latin-1') write_file(name, text) @@ -507,8 +505,13 @@ describe("spell checking with 'encoding' set to utf-8", function() -- Vim function in the original legacy test. local function test_one(aff, dic) -- Generate a .spl file from a .dic and .aff file. - os.execute('cp -f Xtest'..aff..'.aff Xtest.aff') - os.execute('cp -f Xtest'..dic..'.dic Xtest.dic') + if helpers.iswin() then + os.execute('copy /y Xtest'..aff..'.aff Xtest.aff') + os.execute('copy /y Xtest'..dic..'.dic Xtest.dic') + else + os.execute('cp -f Xtest'..aff..'.aff Xtest.aff') + os.execute('cp -f Xtest'..dic..'.dic Xtest.dic') + end source([[ set spellfile= function! SpellDumpNoShow() @@ -559,7 +562,11 @@ describe("spell checking with 'encoding' set to utf-8", function() feed_command([[$put =soundfold('kóopërÿnôven')]]) feed_command([[$put =soundfold('oeverloos gezwets edale')]]) -- And now with SAL instead of SOFO items; test automatic reloading. - os.execute('cp -f Xtest-sal.aff Xtest.aff') + if helpers.iswin() then + os.execute('copy /y Xtest-sal.aff Xtest.aff') + else + os.execute('cp -f Xtest-sal.aff Xtest.aff') + end feed_command('mkspell! Xtest Xtest') feed_command([[$put =soundfold('goobledygoook')]]) feed_command([[$put =soundfold('kóopërÿnôven')]]) diff --git a/test/functional/legacy/063_match_and_matchadd_spec.lua b/test/functional/legacy/063_match_and_matchadd_spec.lua index a505a2db30..518d79861b 100644 --- a/test/functional/legacy/063_match_and_matchadd_spec.lua +++ b/test/functional/legacy/063_match_and_matchadd_spec.lua @@ -114,9 +114,11 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( command("call clearmatches()") eq('\nE714: List required', redir_exec("let rf1 = setmatches(0)")) eq(-1, eval('rf1')) - eq('\nE474: Invalid argument', redir_exec("let rf2 = setmatches([0])")) + eq('\nE474: List item 0 is either not a dictionary or an empty one', + redir_exec("let rf2 = setmatches([0])")) eq(-1, eval('rf2')) - eq('\nE474: Invalid argument', redir_exec("let rf3 = setmatches([{'wrong key': 'wrong value'}])")) + eq('\nE474: List item 0 is missing one of the required keys', + redir_exec("let rf3 = setmatches([{'wrong key': 'wrong value'}])")) eq(-1, eval('rf3')) -- Check that "matchaddpos()" positions matches correctly diff --git a/test/functional/legacy/077_mf_hash_grow_spec.lua b/test/functional/legacy/077_mf_hash_grow_spec.lua index c692127213..4719a3ecbf 100644 --- a/test/functional/legacy/077_mf_hash_grow_spec.lua +++ b/test/functional/legacy/077_mf_hash_grow_spec.lua @@ -18,7 +18,8 @@ describe('mf_hash_grow()', function() setup(clear) -- Check to see if cksum exists, otherwise skip the test - if os.execute('which cksum 2>&1 > /dev/null') ~= 0 then + local null = helpers.iswin() and 'nul' or '/dev/null' + if os.execute('cksum --help >' .. null .. ' 2>&1') ~= 0 then pending('was not tested because cksum was not found', function() end) else it('is working', function() diff --git a/test/functional/legacy/089_number_relnumber_findfile_spec.lua b/test/functional/legacy/089_number_relnumber_findfile_spec.lua deleted file mode 100644 index 6708fd50b7..0000000000 --- a/test/functional/legacy/089_number_relnumber_findfile_spec.lua +++ /dev/null @@ -1,116 +0,0 @@ --- - Some tests for setting 'number' and 'relativenumber' --- This is not all that useful now that the options are no longer reset when --- setting the other. - -local helpers = require('test.functional.helpers')(after_each) -local feed = helpers.feed -local clear, expect, source = helpers.clear, helpers.expect, helpers.source - -describe("setting 'number' and 'relativenumber'", function() - setup(clear) - - it('is working', function() - source([[ - set hidden nu rnu - redir @a | set nu? | set rnu? | redir END - e! xx - redir @b | set nu? | set rnu? | redir END - e! # - $put ='results:' - $put a - $put b - - set nonu nornu - setglobal nu - setlocal rnu - redir @c | setglobal nu? | redir END - set nonu nornu - setglobal rnu - setlocal nu - redir @d | setglobal rnu? | redir END - $put =':setlocal must NOT reset the other global value' - $put c - $put d - - set nonu nornu - setglobal nu - setglobal rnu - redir @e | setglobal nu? | redir END - set nonu nornu - setglobal rnu - setglobal nu - redir @f | setglobal rnu? | redir END - $put =':setglobal MUST reset the other global value' - $put e - $put f - - set nonu nornu - set nu - set rnu - redir @g | setglobal nu? | redir END - set nonu nornu - set rnu - set nu - redir @h | setglobal rnu? | redir END - $put =':set MUST reset the other global value' - $put g - $put h - ]]) - - -- Remove empty line - feed('ggdd') - - -- Assert buffer contents. - expect([[ - results: - - number - relativenumber - - number - relativenumber - :setlocal must NOT reset the other global value - - number - - relativenumber - :setglobal MUST reset the other global value - - number - - relativenumber - :set MUST reset the other global value - - number - - relativenumber]]) - end) -end) - --- - Some tests for findfile() function -describe('findfile', function() - setup(clear) - - it('is working', function() - -- Assume test is being run from project root - source([[ - $put ='Testing findfile' - $put ='' - set ssl - $put =findfile('vim.c','src/nvim/ap*') - cd src/nvim - $put =findfile('vim.c','ap*') - $put =findfile('vim.c','api') - ]]) - - -- Remove empty line - feed('ggdd') - - expect([[ - Testing findfile - - src/nvim/api/vim.c - api/vim.c - api/vim.c]]) - end) -end) diff --git a/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua b/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua index b1221ff8b6..f09fd9a6e5 100644 --- a/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua +++ b/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua @@ -7,8 +7,6 @@ local helpers = require('test.functional.helpers')(after_each) local feed, insert = helpers.feed, helpers.insert local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect -if helpers.pending_win32(pending) then return end - describe('store cursor position in session file in Latin-1', function() setup(clear) diff --git a/test/functional/legacy/096_location_list_spec.lua b/test/functional/legacy/096_location_list_spec.lua index 85c4fe0ec4..b21a2085f6 100644 --- a/test/functional/legacy/096_location_list_spec.lua +++ b/test/functional/legacy/096_location_list_spec.lua @@ -11,10 +11,10 @@ local source = helpers.source local clear, command, expect = helpers.clear, helpers.command, helpers.expect describe('location list', function() + local test_file = 'Xtest-096_location_list.out' setup(clear) - teardown(function() - os.remove('test.out') + os.remove(test_file) end) it('is working', function() @@ -70,9 +70,9 @@ describe('location list', function() endfor ]]) - -- Set up the result buffer "test.out". + -- Set up the result buffer. command('enew') - command('w! test.out') + command('w! '..test_file) command('b 1') -- Test A. @@ -99,7 +99,7 @@ describe('location list', function() command([[let locationListFileName = substitute(getline(line('.')), '\([^|]*\)|.*', '\1', '')]]) command('wincmd n') command('wincmd K') - command('b test.out') + command('b '..test_file) -- Prepare test output and write it to the result buffer. command([[let fileName = substitute(fileName, '\\', '/', 'g')]]) @@ -132,7 +132,7 @@ describe('location list', function() command('let numberOfWindowsOpen = winnr("$")') command('wincmd n') command('wincmd K') - command('b test.out') + command('b '..test_file) -- Prepare test output and write it to the result buffer. command('call append(line("$"), "Test B:")') @@ -170,7 +170,7 @@ describe('location list', function() command('let bufferName = expand("%")') command('wincmd n') command('wincmd K') - command('b test.out') + command('b '..test_file) -- Prepare test output and write it to the result buffer. command([[let bufferName = substitute(bufferName, '\\', '/', 'g')]]) diff --git a/test/functional/legacy/097_glob_path_spec.lua b/test/functional/legacy/097_glob_path_spec.lua index 6b63a317f1..907f0665ae 100644 --- a/test/functional/legacy/097_glob_path_spec.lua +++ b/test/functional/legacy/097_glob_path_spec.lua @@ -6,15 +6,19 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local command, expect = helpers.command, helpers.expect -if helpers.pending_win32(pending) then return end - describe('glob() and globpath()', function() setup(clear) setup(function() - os.execute("mkdir -p sautest/autoload") - os.execute("touch sautest/autoload/Test104.vim") - os.execute("touch sautest/autoload/footest.vim") + if helpers.iswin() then + os.execute("md sautest\\autoload") + os.execute(".>sautest\\autoload\\Test104.vim 2>nul") + os.execute(".>sautest\\autoload\\footest.vim 2>nul") + else + os.execute("mkdir -p sautest/autoload") + os.execute("touch sautest/autoload/Test104.vim") + os.execute("touch sautest/autoload/footest.vim") + end end) it('is working', function() @@ -24,29 +28,55 @@ describe('glob() and globpath()', function() -- Consistent sorting of file names command('set nofileignorecase') - command([[$put =glob('Xxx\{')]]) - command([[$put =glob('Xxx\$')]]) + if helpers.iswin() then + command([[$put =glob('Xxx{')]]) + command([[$put =glob('Xxx$')]]) + + command('silent w! Xxx{') + command([[w! Xxx$]]) + command([[$put =glob('Xxx{')]]) + command([[$put =glob('Xxx$')]]) + + command([[$put =string(globpath('sautest\autoload', '*.vim'))]]) + command([[$put =string(globpath('sautest\autoload', '*.vim', 0, 1))]]) + expect([=[ + + - command('silent w! Xxx{') - command([[w! Xxx\$]]) - command([[$put =glob('Xxx\{')]]) - command([[$put =glob('Xxx\$')]]) + Xxx{ + Xxx$ + 'sautest\autoload\Test104.vim + sautest\autoload\footest.vim' + ['sautest\autoload\Test104.vim', 'sautest\autoload\footest.vim']]=]) + else + command([[$put =glob('Xxx\{')]]) + command([[$put =glob('Xxx\$')]]) - command("$put =string(globpath('sautest/autoload', '*.vim'))") - command("$put =string(globpath('sautest/autoload', '*.vim', 0, 1))") + command('silent w! Xxx{') + command([[w! Xxx\$]]) + command([[$put =glob('Xxx\{')]]) + command([[$put =glob('Xxx\$')]]) - expect([=[ + command("$put =string(globpath('sautest/autoload', '*.vim'))") + command("$put =string(globpath('sautest/autoload', '*.vim', 0, 1))") + expect([=[ - Xxx{ - Xxx$ - 'sautest/autoload/Test104.vim - sautest/autoload/footest.vim' - ['sautest/autoload/Test104.vim', 'sautest/autoload/footest.vim']]=]) + Xxx{ + Xxx$ + 'sautest/autoload/Test104.vim + sautest/autoload/footest.vim' + ['sautest/autoload/Test104.vim', 'sautest/autoload/footest.vim']]=]) + end end) teardown(function() - os.execute("rm -rf sautest Xxx{ Xxx$") + if helpers.iswin() then + os.execute('del /q/f Xxx{ Xxx$') + os.execute('rd /q sautest') + else + os.execute("rm -rf sautest Xxx{ Xxx$") + end end) end) diff --git a/test/functional/legacy/107_adjust_window_and_contents_spec.lua b/test/functional/legacy/107_adjust_window_and_contents_spec.lua index 836a0f8f24..239f60341a 100644 --- a/test/functional/legacy/107_adjust_window_and_contents_spec.lua +++ b/test/functional/legacy/107_adjust_window_and_contents_spec.lua @@ -8,8 +8,6 @@ local clear = helpers.clear local insert = helpers.insert local command = helpers.command -if helpers.pending_win32(pending) then return end - describe('107', function() setup(clear) diff --git a/test/functional/legacy/arglist_spec.lua b/test/functional/legacy/arglist_spec.lua index 191f145095..bd65e549ef 100644 --- a/test/functional/legacy/arglist_spec.lua +++ b/test/functional/legacy/arglist_spec.lua @@ -4,8 +4,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, command, eq = helpers.clear, helpers.command, helpers.eq local eval, exc_exec, neq = helpers.eval, helpers.exc_exec, helpers.neq -if helpers.pending_win32(pending) then return end - describe('argument list commands', function() before_each(clear) @@ -222,20 +220,19 @@ describe('argument list commands', function() eq({'a', 'b'}, eval('argv()')) eq('b', eval('expand("%:t")')) command('argedit a') - eq({'a', 'b'}, eval('argv()')) + eq({'a', 'b', 'a'}, eval('argv()')) eq('a', eval('expand("%:t")')) command('argedit c') - eq({'a', 'c', 'b'}, eval('argv()')) + eq({'a', 'b', 'a', 'c'}, eval('argv()')) command('0argedit x') - eq({'x', 'a', 'c', 'b'}, eval('argv()')) + eq({'x', 'a', 'b', 'a', 'c'}, eval('argv()')) command('enew! | set modified') assert_fails('argedit y', 'E37:') command('argedit! y') - eq({'x', 'y', 'a', 'c', 'b'}, eval('argv()')) + eq({'x', 'y', 'y', 'a', 'b', 'a', 'c'}, eval('argv()')) command('%argd') - -- Nvim allows unescaped spaces in filename on all platforms. #6010 command('argedit a b') - eq({'a b'}, eval('argv()')) + eq({'a', 'b'}, eval('argv()')) end) it('test for :argdelete command', function() diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua index aeaab335e8..5ef456bfe3 100644 --- a/test/functional/legacy/delete_spec.lua +++ b/test/functional/legacy/delete_spec.lua @@ -2,8 +2,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, source = helpers.clear, helpers.source local eq, eval, command = helpers.eq, helpers.eval, helpers.command -if helpers.pending_win32(pending) then return end - describe('Test for delete()', function() before_each(clear) @@ -48,7 +46,11 @@ describe('Test for delete()', function() split Xfile call setline(1, ['a', 'b']) wq - silent !ln -s Xfile Xlink + if has('win32') + silent !mklink Xlink Xfile + else + silent !ln -s Xfile Xlink + endif ]]) -- Delete the link, not the file eq(0, eval("delete('Xlink')")) @@ -58,7 +60,11 @@ describe('Test for delete()', function() it('symlink directory delete', function() command("call mkdir('Xdir1')") - command("silent !ln -s Xdir1 Xlink") + if helpers.iswin() then + command("silent !mklink /j Xlink Xdir1") + else + command("silent !ln -s Xdir1 Xlink") + end eq(1, eval("isdirectory('Xdir1')")) eq(1, eval("isdirectory('Xlink')")) -- Delete the link, not the directory @@ -78,7 +84,11 @@ describe('Test for delete()', function() w Xdir3/subdir/Xfile w Xdir4/Xfile close - silent !ln -s ../Xdir4 Xdir3/Xlink + if has('win32') + silent !mklink /j Xdir3\Xlink Xdir4 + else + silent !ln -s ../Xdir4 Xdir3/Xlink + endif ]]) eq(1, eval("isdirectory('Xdir3')")) diff --git a/test/functional/legacy/edit_spec.lua b/test/functional/legacy/edit_spec.lua new file mode 100644 index 0000000000..91d602924c --- /dev/null +++ b/test/functional/legacy/edit_spec.lua @@ -0,0 +1,25 @@ +-- Test for edit functions +-- See also: src/nvim/testdir/test_edit.vim + +local helpers = require('test.functional.helpers')(after_each) +local source = helpers.source +local eq, eval = helpers.eq, helpers.eval +local funcs = helpers.funcs +local clear = helpers.clear + +describe('edit', function() + before_each(clear) + + it('reset insertmode from i_ctrl-r_=', function() + source([=[ + call setline(1, ['abc']) + call cursor(1, 4) + call feedkeys(":set im\<cr>ZZZ\<c-r>=setbufvar(1,'&im', 0)\<cr>",'tnix') + ]=]) + eq({'abZZZc'}, funcs.getline(1,'$')) + eq({0, 1, 1, 0}, funcs.getpos('.')) + eq(0, eval('&im')) + end) + +end) + diff --git a/test/functional/legacy/fixeol_spec.lua b/test/functional/legacy/fixeol_spec.lua index 801451b300..50236e8617 100644 --- a/test/functional/legacy/fixeol_spec.lua +++ b/test/functional/legacy/fixeol_spec.lua @@ -4,15 +4,14 @@ local helpers = require('test.functional.helpers')(after_each) local feed = helpers.feed local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect -if helpers.pending_win32(pending) then return end - describe('fixeol', function() local function rmtestfiles() - os.remove('test.out') - os.remove('XXEol') - os.remove('XXNoEol') - os.remove('XXTestEol') - os.remove('XXTestNoEol') + feed_command('%bwipeout!') + feed_command('call delete("test.out")') + feed_command('call delete("XXEol")') + feed_command('call delete("XXNoEol")') + feed_command('call delete("XXTestEol")') + feed_command('call delete("XXTestNoEol")') end setup(function() clear() diff --git a/test/functional/legacy/fnamemodify_spec.lua b/test/functional/legacy/fnamemodify_spec.lua index d8ecbfe058..7e859bf0cf 100644 --- a/test/functional/legacy/fnamemodify_spec.lua +++ b/test/functional/legacy/fnamemodify_spec.lua @@ -4,8 +4,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, source = helpers.clear, helpers.source local call, eq, nvim = helpers.call, helpers.eq, helpers.meths -if helpers.pending_win32(pending) then return end - local function expected_empty() eq({}, nvim.get_vvar('errors')) end @@ -16,17 +14,21 @@ describe('filename modifiers', function() source([=[ func Test_fnamemodify() - let tmpdir = resolve('/tmp') + if has('win32') + set shellslash + else + set shell=sh + endif + let tmpdir = resolve($TMPDIR) + call assert_true(isdirectory(tmpdir)) execute 'cd '. tmpdir - set shell=sh - set shellslash let $HOME=fnamemodify('.', ':p:h:h:h') call assert_equal('/', fnamemodify('.', ':p')[-1:]) - call assert_equal('p', fnamemodify('.', ':p:h')[-1:]) + call assert_equal(tmpdir[strchars(tmpdir) - 1], fnamemodify('.', ':p:h')[-1:]) call assert_equal('t', fnamemodify('test.out', ':p')[-1:]) call assert_equal('test.out', fnamemodify('test.out', ':.')) call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':.')) - call assert_equal('test.out', fnamemodify('test.out', ':~')) + call assert_equal(fnamemodify(tmpdir, ':~').'/test.out', fnamemodify('test.out', ':~')) call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':~')) call assert_equal('a', fnamemodify('../testdir/a', ':t')) call assert_equal('', fnamemodify('.', ':p:t')) @@ -53,8 +55,10 @@ describe('filename modifiers', function() quit call assert_equal("'abc\ndef'", fnamemodify("abc\ndef", ':S')) - set shell=tcsh - call assert_equal("'abc\\\ndef'", fnamemodify("abc\ndef", ':S')) + if executable('tcsh') + set shell=tcsh + call assert_equal("'abc\\\ndef'", fnamemodify("abc\ndef", ':S')) + endif endfunc func Test_expand() diff --git a/test/functional/legacy/getcwd_spec.lua b/test/functional/legacy/getcwd_spec.lua index 8fb31ccd22..eae13da528 100644 --- a/test/functional/legacy/getcwd_spec.lua +++ b/test/functional/legacy/getcwd_spec.lua @@ -4,8 +4,6 @@ local helpers = require('test.functional.helpers')(after_each) local eq, eval, source = helpers.eq, helpers.eval, helpers.source local call, clear, command = helpers.call, helpers.clear, helpers.command -if helpers.pending_win32(pending) then return end - describe('getcwd', function() before_each(clear) diff --git a/test/functional/legacy/packadd_spec.lua b/test/functional/legacy/packadd_spec.lua index 2dfd36142b..fb308475c0 100644 --- a/test/functional/legacy/packadd_spec.lua +++ b/test/functional/legacy/packadd_spec.lua @@ -9,17 +9,15 @@ local function expected_empty() eq({}, nvim.get_vvar('errors')) end -if helpers.pending_win32(pending) then return end - describe('packadd', function() before_each(function() clear() source([=[ func SetUp() - let s:topdir = expand('%:p:h') . '/Xdir' + let s:topdir = expand(expand('%:p:h') . '/Xdir') exe 'set packpath=' . s:topdir - let s:plugdir = s:topdir . '/pack/mine/opt/mytest' + let s:plugdir = expand(s:topdir . '/pack/mine/opt/mytest') endfunc func TearDown() @@ -52,8 +50,8 @@ describe('packadd', function() call assert_equal(77, g:plugin_also_works) call assert_true(17, g:ftdetect_works) call assert_true(len(&rtp) > len(rtp)) - call assert_true(&rtp =~ (s:plugdir . '\($\|,\)')) - call assert_true(&rtp =~ (s:plugdir . '/after$')) + call assert_true(&rtp =~ (escape(s:plugdir, '\') . '\($\|,\)')) + call assert_true(&rtp =~ escape(expand(s:plugdir . '/after$'), '\')) " Check exception call assert_fails("packadd directorynotfound", 'E919:') @@ -74,7 +72,7 @@ describe('packadd', function() packadd! mytest call assert_true(len(&rtp) > len(rtp)) - call assert_true(&rtp =~ (s:plugdir . '\($\|,\)')) + call assert_true(&rtp =~ (escape(s:plugdir, '\') . '\($\|,\)')) call assert_equal(0, g:plugin_works) " check the path is not added twice @@ -84,17 +82,18 @@ describe('packadd', function() endfunc func Test_packadd_symlink_dir() - if !has('unix') - return - endif - let top2_dir = s:topdir . '/Xdir2' - let real_dir = s:topdir . '/Xsym' + let top2_dir = expand(s:topdir . '/Xdir2') + let real_dir = expand(s:topdir . '/Xsym') call mkdir(real_dir, 'p') - exec "silent! !ln -s Xsym" top2_dir - let &rtp = top2_dir . ',' . top2_dir . '/after' + if has('win32') + exec "silent! !mklink /d" top2_dir "Xsym" + else + exec "silent! !ln -s Xsym" top2_dir + endif + let &rtp = top2_dir . ',' . expand(top2_dir . '/after') let &packpath = &rtp - let s:plugdir = top2_dir . '/pack/mine/opt/mytest' + let s:plugdir = expand(top2_dir . '/pack/mine/opt/mytest') call mkdir(s:plugdir . '/plugin', 'p') exe 'split ' . s:plugdir . '/plugin/test.vim' @@ -105,7 +104,7 @@ describe('packadd', function() packadd mytest " Must have been inserted in the middle, not at the end - call assert_true(&rtp =~ '/pack/mine/opt/mytest,') + call assert_true(&rtp =~ escape(expand('/pack/mine/opt/mytest').',', '\')) call assert_equal(44, g:plugin_works) " No change when doing it again. @@ -115,7 +114,7 @@ describe('packadd', function() set rtp& let rtp = &rtp - exec "silent !rm" top2_dir + exec "silent !" (has('win32') ? "rd /q/s" : "rm") top2_dir endfunc func Test_packloadall() diff --git a/test/functional/legacy/search_spec.lua b/test/functional/legacy/search_spec.lua index 5f71861821..277d8d6c7f 100644 --- a/test/functional/legacy/search_spec.lua +++ b/test/functional/legacy/search_spec.lua @@ -6,6 +6,7 @@ local eq = helpers.eq local eval = helpers.eval local feed = helpers.feed local funcs = helpers.funcs +local wait = helpers.wait describe('search cmdline', function() local screen @@ -471,4 +472,113 @@ describe('search cmdline', function() coladd = 0, skipcol = 0, curswant = 0}, funcs.winsaveview()) end) + + it("CTRL-G with 'incsearch' and ? goes in the right direction", function() + -- oldtest: Test_search_cmdline4(). + screen:detach() + screen = Screen.new(40, 4) + screen:attach() + screen:set_default_attr_ids({ + inc = {reverse = true}, + err = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, + more = { bold = true, foreground = Screen.colors.SeaGreen4 }, + tilde = { bold = true, foreground = Screen.colors.Blue1 }, + }) + command('enew!') + funcs.setline(1, {' 1 the first', ' 2 the second', ' 3 the third'}) + command('set laststatus=0 shortmess+=s') + command('set incsearch') + command('$') + -- Send the input in chunks, so the cmdline logic regards it as + -- "interactive". This mimics Vim's test_override("char_avail"). + -- (See legacy test: test_search.vim) + feed('?the') + wait() + feed('<c-g>') + wait() + feed('<cr>') + screen:expect([[ + 1 the first | + 2 the second | + 3 ^the third | + ?the | + ]]) + + command('$') + feed('?the') + wait() + feed('<c-g>') + wait() + feed('<c-g>') + wait() + feed('<cr>') + screen:expect([[ + 1 ^the first | + 2 the second | + 3 the third | + ?the | + ]]) + + command('$') + feed('?the') + wait() + feed('<c-g>') + wait() + feed('<c-g>') + wait() + feed('<c-g>') + wait() + feed('<cr>') + screen:expect([[ + 1 the first | + 2 ^the second | + 3 the third | + ?the | + ]]) + + command('$') + feed('?the') + wait() + feed('<c-t>') + wait() + feed('<cr>') + screen:expect([[ + 1 ^the first | + 2 the second | + 3 the third | + ?the | + ]]) + + command('$') + feed('?the') + wait() + feed('<c-t>') + wait() + feed('<c-t>') + wait() + feed('<cr>') + screen:expect([[ + 1 the first | + 2 the second | + 3 ^the third | + ?the | + ]]) + + command('$') + feed('?the') + wait() + feed('<c-t>') + wait() + feed('<c-t>') + wait() + feed('<c-t>') + wait() + feed('<cr>') + screen:expect([[ + 1 the first | + 2 ^the second | + 3 the third | + ?the | + ]]) + end) end) diff --git a/test/functional/legacy/wordcount_spec.lua b/test/functional/legacy/wordcount_spec.lua index 5412903866..0c8bd2cdcc 100644 --- a/test/functional/legacy/wordcount_spec.lua +++ b/test/functional/legacy/wordcount_spec.lua @@ -6,8 +6,6 @@ local clear, command = helpers.clear, helpers.command local eq, eval = helpers.eq, helpers.eval local wait = helpers.wait -if helpers.pending_win32(pending) then return end - describe('wordcount', function() before_each(clear) diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua index 8ca5fe57ba..007d40874f 100644 --- a/test/functional/lua/overrides_spec.lua +++ b/test/functional/lua/overrides_spec.lua @@ -87,6 +87,7 @@ describe('debug.debug', function() E = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, cr = {bold = true, foreground = Screen.colors.SeaGreen4}, }) + command("set display-=msgsep") end) it('works', function() command([[lua diff --git a/test/functional/normal/K_spec.lua b/test/functional/normal/K_spec.lua index 43e598633c..174313d80e 100644 --- a/test/functional/normal/K_spec.lua +++ b/test/functional/normal/K_spec.lua @@ -2,8 +2,6 @@ local helpers = require('test.functional.helpers')(after_each) local eq, clear, eval, feed = helpers.eq, helpers.clear, helpers.eval, helpers.feed -if helpers.pending_win32(pending) then return end - describe('K', function() local test_file = 'K_spec_out' before_each(function() @@ -29,7 +27,7 @@ describe('K', function() it("invokes non-prefixed 'keywordprg' as shell command", function() helpers.source([[ let @a='fnord' - set keywordprg=echo\ fnord\ >>]]) + set keywordprg=echo\ fnord>>]]) -- K on the text "K_spec_out" resolves to `!echo fnord >> K_spec_out`. feed('i'..test_file..'<ESC>K') diff --git a/test/functional/normal/langmap_spec.lua b/test/functional/normal/langmap_spec.lua new file mode 100644 index 0000000000..e4349a22e7 --- /dev/null +++ b/test/functional/normal/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<esc>') + 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<esc>') + 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<esc>') + 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('<C-w>s') + local origwin = curwin() + feed('<C-w>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('a<C-r>i<esc>') + expect('illii www') + feed('"ip') + expect('illllii www') + -- Works with i_CTRL-O + feed('0a<C-O>ihi<esc>') + 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|ahello<C-G>jx<esc>') + feed('q') + expect([[ + helhellolo + helxlo + hello]]) + eq(eval('@a'), 'gg3|ahellojx') + end) + it('command-line CTRL-\\', function() + command('set langmap=en,ne') + feed(':<C-\\>e\'hello\'\r<C-B>put ="<C-E>"<CR>') + 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 <C-R>i=1<CR>') + 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()<CR> + ]]) + feed('x1<CR>') + eq(eval('gotten_one'), 1) + command('let g:gotten_one = 0') + feed_command('call Map()') + feed('1<CR>') + 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('/iii<cr>x') + expect('ii www') + end) + it('conversions not applied in cmdline mode', function() + feed(':call append(1, "iii")<cr>') + 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<BS><esc>', 'hell', local_setup) + testrecording('>><lt><lt>', 'hello', local_setup) + command('nnoremap \\ x') + testrecording('\\', 'ello', local_setup) + testrecording('A<C-V><BS><esc>', 'hello<BS>', local_setup) + end) + pending('Translates modified keys correctly', function() + command('nnoremap <M-i> x') + command('nnoremap <M-w> l') + testrecording('<M-w>', 'ello', local_setup) + testrecording('<M-i>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<esc>') + 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 <C-w> iw<esc>') + command('nnoremap <C-i> ii<esc>') + testrecording('<C-w>', 'whello', local_setup) + testrecording('<C-i>', 'ihello', local_setup) + end) + +end) diff --git a/test/functional/options/autochdir_spec.lua b/test/functional/options/autochdir_spec.lua index 209531515c..2fce0a5ed9 100644 --- a/test/functional/options/autochdir_spec.lua +++ b/test/functional/options/autochdir_spec.lua @@ -3,8 +3,6 @@ local clear = helpers.clear local eq = helpers.eq local getcwd = helpers.funcs.getcwd -if helpers.pending_win32(pending) then return end - describe("'autochdir'", function() it('given on the shell gets processed properly', function() local targetdir = 'test/functional/fixtures' @@ -12,9 +10,10 @@ describe("'autochdir'", function() -- By default 'autochdir' is off, thus getcwd() returns the repo root. clear(targetdir..'/tty-test.c') local rootdir = getcwd() + local expected = rootdir .. '/' .. targetdir -- With 'autochdir' on, we should get the directory of tty-test.c. clear('--cmd', 'set autochdir', targetdir..'/tty-test.c') - eq(rootdir..'/'..targetdir, getcwd()) + eq(helpers.iswin() and expected:gsub('/', '\\') or expected, getcwd()) end) end) diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index b83b7b8eee..9e29baba2d 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -7,23 +7,13 @@ local command = helpers.command local clear = helpers.clear local eval = helpers.eval local eq = helpers.eq +local insert = helpers.insert local neq = helpers.neq local mkdir = helpers.mkdir local rmdir = helpers.rmdir -local function init_session(...) - local args = { helpers.nvim_prog, '-i', 'NONE', '--embed', - '--cmd', helpers.nvim_set } - for _, v in ipairs({...}) do - table.insert(args, v) - end - helpers.set_session(helpers.spawn(args)) -end - describe('startup defaults', function() describe(':filetype', function() - if helpers.pending_win32(pending) then return end - local function expect_filetype(expected) local screen = Screen.new(50, 4) screen:attach() @@ -36,50 +26,70 @@ describe('startup defaults', function() ) end - it('enabled by `-u NORC`', function() - init_session('-u', 'NORC') + it('all ON after `-u NORC`', function() + clear('-u', 'NORC') expect_filetype( 'filetype detection:ON plugin:ON indent:ON |') end) - it('disabled by `-u NONE`', function() - init_session('-u', 'NONE') + it('all ON after `:syntax …` #7765', function() + clear('-u', 'NORC', '--cmd', 'syntax on') expect_filetype( - 'filetype detection:OFF plugin:OFF indent:OFF |') + 'filetype detection:ON plugin:ON indent:ON |') + clear('-u', 'NORC', '--cmd', 'syntax off') + expect_filetype( + 'filetype detection:ON plugin:ON indent:ON |') end) - it('overridden by early `filetype on`', function() - init_session('-u', 'NORC', '--cmd', 'filetype on') + it('all OFF after `-u NONE`', function() + clear('-u', 'NONE') expect_filetype( - 'filetype detection:ON plugin:OFF indent:OFF |') + 'filetype detection:OFF plugin:OFF indent:OFF |') end) - it('overridden by early `filetype plugin on`', function() - init_session('-u', 'NORC', '--cmd', 'filetype plugin on') + it('explicit OFF stays OFF', function() + clear('-u', 'NORC', '--cmd', + 'syntax off | filetype off | filetype plugin indent off') + expect_filetype( + 'filetype detection:OFF plugin:OFF indent:OFF |') + clear('-u', 'NORC', '--cmd', 'syntax off | filetype plugin indent off') + expect_filetype( + 'filetype detection:ON plugin:OFF indent:OFF |') + clear('-u', 'NORC', '--cmd', 'filetype indent off') expect_filetype( 'filetype detection:ON plugin:ON indent:OFF |') + clear('-u', 'NORC', '--cmd', 'syntax off | filetype off') + expect_filetype( + 'filetype detection:OFF plugin:(on) indent:(on) |') + -- Swap the order. + clear('-u', 'NORC', '--cmd', 'filetype off | syntax off') + expect_filetype( + 'filetype detection:OFF plugin:(on) indent:(on) |') end) - it('overridden by early `filetype indent on`', function() - init_session('-u', 'NORC', '--cmd', 'filetype indent on') + it('all ON after early `:filetype … on`', function() + -- `:filetype … on` should not change the defaults. #7765 + -- Only an explicit `:filetype … off` sets OFF. + + clear('-u', 'NORC', '--cmd', 'filetype on') expect_filetype( - 'filetype detection:ON plugin:OFF indent:ON |') + 'filetype detection:ON plugin:ON indent:ON |') + clear('-u', 'NORC', '--cmd', 'filetype plugin on') + expect_filetype( + 'filetype detection:ON plugin:ON indent:ON |') + clear('-u', 'NORC', '--cmd', 'filetype indent on') + expect_filetype( + 'filetype detection:ON plugin:ON indent:ON |') end) - it('adjusted by late `filetype off`', function() - init_session('-u', 'NORC', '-c', 'filetype off') + it('late `:filetype … off` stays OFF', function() + clear('-u', 'NORC', '-c', 'filetype off') expect_filetype( 'filetype detection:OFF plugin:(on) indent:(on) |') - end) - - it('adjusted by late `filetype plugin off`', function() - init_session('-u', 'NORC', '-c', 'filetype plugin off') + clear('-u', 'NORC', '-c', 'filetype plugin off') expect_filetype( 'filetype detection:ON plugin:OFF indent:ON |') - end) - - it('adjusted by late `filetype indent off`', function() - init_session('-u', 'NORC', '-c', 'filetype indent off') + clear('-u', 'NORC', '-c', 'filetype indent off') expect_filetype( 'filetype detection:ON plugin:ON indent:OFF |') end) @@ -87,27 +97,59 @@ describe('startup defaults', function() describe('syntax', function() it('enabled by `-u NORC`', function() - init_session('-u', 'NORC') + clear('-u', 'NORC') eq(1, eval('g:syntax_on')) end) it('disabled by `-u NONE`', function() - init_session('-u', 'NONE') + clear('-u', 'NONE') eq(0, eval('exists("g:syntax_on")')) end) - it('overridden by early `syntax off`', function() - init_session('-u', 'NORC', '--cmd', 'syntax off') + it('`:syntax off` stays off', function() + -- early + clear('-u', 'NORC', '--cmd', 'syntax off') + eq(0, eval('exists("g:syntax_on")')) + -- late + clear('-u', 'NORC', '-c', 'syntax off') eq(0, eval('exists("g:syntax_on")')) end) + end) - it('adjusted by late `syntax off`', function() - init_session('-u', 'NORC', '-c', 'syntax off') - eq(0, eval('exists("g:syntax_on")')) + describe("'fillchars'", function() + it('vert/fold flags', function() + clear() + local screen = Screen.new(50, 5) + screen:attach() + command('set laststatus=0') + insert([[ + 1 + 2 + 3 + 4]]) + command('normal! ggjzfj') + command('vsp') + screen:expect([[ + 1 │1 | + ^+-- 2 lines: 2··········│+-- 2 lines: 2·········| + 4 │4 | + ~ │~ | + | + ]]) + + -- ambiwidth=double defaults to single-byte fillchars. + command('set ambiwidth=double') + screen:expect([[ + 1 |1 | + ^+-- 2 lines: 2----------|+-- 2 lines: 2---------| + 4 |4 | + ~ |~ | + | + ]]) end) end) - describe('packpath', function() + describe("'packpath'", function() it('defaults to &runtimepath', function() eq(meths.get_option('runtimepath'), meths.get_option('packpath')) end) diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua new file mode 100644 index 0000000000..ed17ffdd3c --- /dev/null +++ b/test/functional/options/num_options_spec.lua @@ -0,0 +1,97 @@ +-- Tests for :setlocal and :setglobal + +local helpers = require('test.functional.helpers')(after_each) +local clear, feed_command, eval, eq, meths = + helpers.clear, helpers.feed_command, helpers.eval, helpers.eq, helpers.meths + +local function should_fail(opt, value, errmsg) + feed_command('setglobal ' .. opt .. '=' .. value) + eq(errmsg, eval("v:errmsg"):match("E%d*")) + feed_command('let v:errmsg = ""') + feed_command('setlocal ' .. opt .. '=' .. value) + eq(errmsg, eval("v:errmsg"):match("E%d*")) + feed_command('let v:errmsg = ""') + local status, err = pcall(meths.set_option, opt, value) + eq(status, false) + eq(errmsg, err:match("E%d*")) + eq('', eval("v:errmsg")) +end + +local function should_succeed(opt, value) + feed_command('setglobal ' .. opt .. '=' .. value) + feed_command('setlocal ' .. opt .. '=' .. value) + meths.set_option(opt, value) + eq(value, meths.get_option(opt)) + eq('', eval("v:errmsg")) +end + +describe(':setlocal', function() + before_each(clear) + + it('setlocal sets only local value', function() + eq(0, meths.get_option('iminsert')) + feed_command('setlocal iminsert=1') + eq(0, meths.get_option('iminsert')) + eq(0, meths.get_option('imsearch')) + feed_command('setlocal imsearch=1') + eq(0, meths.get_option('imsearch')) + end) +end) + +describe(':set validation', function() + before_each(clear) + + it('setlocal and setglobal validate values', function() + should_fail('shiftwidth', -10, 'E487') + should_succeed('shiftwidth', 0) + should_fail('tabstop', -10, 'E487') + should_fail('winheight', -10, 'E487') + should_fail('winheight', 0, 'E487') + should_fail('winminheight', -1, 'E487') + should_succeed('winminheight', 0) + should_fail('winwidth', 0, 'E487') + should_fail('helpheight', -1, 'E487') + should_fail('maxcombine', 7, 'E474') + should_fail('iminsert', 3, 'E474') + should_fail('imsearch', 3, 'E474') + should_fail('titlelen', -1, 'E487') + should_fail('cmdheight', 0, 'E487') + should_fail('updatecount', -1, 'E487') + should_fail('textwidth', -1, 'E487') + should_fail('tabstop', 0, 'E487') + should_fail('timeoutlen', -1, 'E487') + should_fail('history', 1000000, 'E474') + should_fail('regexpengine', -1, 'E474') + should_fail('regexpengine', 3, 'E474') + should_succeed('regexpengine', 2) + should_fail('report', -1, 'E487') + should_succeed('report', 0) + should_fail('scrolloff', -1, 'E49') + should_fail('sidescrolloff', -1, 'E487') + should_fail('sidescroll', -1, 'E487') + should_fail('cmdwinheight', 0, 'E487') + should_fail('updatetime', -1, 'E487') + + should_fail('foldlevel', -5, 'E487') + should_fail('foldcolumn', 13, 'E474') + should_fail('conceallevel', 4, 'E474') + should_fail('numberwidth', 11, 'E474') + should_fail('numberwidth', 0, 'E487') + + -- If smaller than 1 this one is set to 'lines'-1 + feed_command('setglobal window=-10') + meths.set_option('window', -10) + eq(23, meths.get_option('window')) + eq('', eval("v:errmsg")) + end) + + it('set wmh/wh wmw/wiw checks', function() + feed_command('set winheight=2') + feed_command('set winminheight=3') + eq('E591', eval("v:errmsg"):match("E%d*")) + + feed_command('set winwidth=2') + feed_command('set winminwidth=3') + eq('E592', eval("v:errmsg"):match("E%d*")) + end) +end) diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua index 8ee0f258d0..f2d5e433db 100644 --- a/test/functional/plugin/health_spec.lua +++ b/test/functional/plugin/health_spec.lua @@ -6,6 +6,7 @@ local clear = helpers.clear local curbuf_contents = helpers.curbuf_contents local command = helpers.command local eq = helpers.eq +local getcompletion = helpers.funcs.getcompletion describe(':checkhealth', function() it("detects invalid $VIMRUNTIME", function() @@ -31,6 +32,11 @@ describe(':checkhealth', function() eq("ERROR: $VIM is invalid: zub", string.match(curbuf_contents(), "ERROR: $VIM .* zub")) end) + it('completions can be listed via getcompletion()', function() + clear() + eq('nvim', getcompletion('nvim', 'checkhealth')[1]) + eq('provider', getcompletion('prov', 'checkhealth')[1]) + end) end) describe('health.vim', function() diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua new file mode 100644 index 0000000000..e5da7932a5 --- /dev/null +++ b/test/functional/plugin/man_spec.lua @@ -0,0 +1,135 @@ +local helpers = require('test.functional.helpers')(after_each) +local plugin_helpers = require('test.functional.plugin.helpers') + +local Screen = require('test.functional.ui.screen') + +local command, eval, rawfeed = helpers.command, helpers.eval, helpers.rawfeed + +before_each(function() + plugin_helpers.reset() + helpers.clear() + command('syntax on') + command('set filetype=man') +end) + +describe(':Man', function() + describe('man.lua: highlight_line()', function() + local screen + + before_each(function() + command('syntax off') -- Ignore syntax groups + screen = Screen.new(52, 5) + screen:set_default_attr_ids({ + b = { bold = true }, + i = { italic = true }, + u = { underline = true }, + bi = { bold = true, italic = true }, + biu = { bold = true, italic = true, underline = true }, + }) + screen:set_default_attr_ignore({ + { foreground = Screen.colors.Blue }, -- control chars + { bold = true, foreground = Screen.colors.Blue } -- empty line '~'s + }) + screen:attach() + end) + + after_each(function() + screen:detach() + end) + + it('clears backspaces from text and adds highlights', function() + rawfeed([[ + ithis i<C-v><C-h>is<C-v><C-h>s a<C-v><C-h>a test + with _<C-v><C-h>o_<C-v><C-h>v_<C-v><C-h>e_<C-v><C-h>r_<C-v><C-h>s_<C-v><C-h>t_<C-v><C-h>r_<C-v><C-h>u_<C-v><C-h>c_<C-v><C-h>k text<ESC>]]) + + screen:expect([[ + this i^His^Hs a^Ha test | + with _^Ho_^Hv_^He_^Hr_^Hs_^Ht_^Hr_^Hu_^Hc_^Hk tex^t | + ~ | + ~ | + | + ]]) + + eval('man#init_pager()') + + screen:expect([[ + ^this {b:is} {b:a} test | + with {u:overstruck} text | + ~ | + ~ | + | + ]]) + end) + + it('clears escape sequences from text and adds highlights', function() + rawfeed([[ + ithis <C-v><ESC>[1mis <C-v><ESC>[3ma <C-v><ESC>[4mtest<C-v><ESC>[0m + <C-v><ESC>[4mwith<C-v><ESC>[24m <C-v><ESC>[4mescaped<C-v><ESC>[24m <C-v><ESC>[4mtext<C-v><ESC>[24m<ESC>]]) + + screen:expect([=[ + this ^[[1mis ^[[3ma ^[[4mtest^[[0m | + ^[[4mwith^[[24m ^[[4mescaped^[[24m ^[[4mtext^[[24^m | + ~ | + ~ | + | + ]=]) + + eval('man#init_pager()') + + screen:expect([[ + ^this {b:is }{bi:a }{biu:test} | + {u:with} {u:escaped} {u:text} | + ~ | + ~ | + | + ]]) + end) + + it('highlights multibyte text', function() + rawfeed([[ + ithis i<C-v><C-h>is<C-v><C-h>s あ<C-v><C-h>あ test + with _<C-v><C-h>ö_<C-v><C-h>v_<C-v><C-h>e_<C-v><C-h>r_<C-v><C-h>s_<C-v><C-h>t_<C-v><C-h>r_<C-v><C-h>u_<C-v><C-h>̃_<C-v><C-h>c_<C-v><C-h>k te<C-v><ESC>[3mxt¶<C-v><ESC>[0m<ESC>]]) + eval('man#init_pager()') + + screen:expect([[ + ^this {b:is} {b:あ} test | + with {u:överstrũck} te{i:xt¶} | + ~ | + ~ | + | + ]]) + end) + + it('highlights underscores based on context', function() + rawfeed([[ + i_<C-v><C-h>_b<C-v><C-h>be<C-v><C-h>eg<C-v><C-h>gi<C-v><C-h>in<C-v><C-h>ns<C-v><C-h>s + m<C-v><C-h>mi<C-v><C-h>id<C-v><C-h>d_<C-v><C-h>_d<C-v><C-h>dl<C-v><C-h>le<C-v><C-h>e + _<C-v><C-h>m_<C-v><C-h>i_<C-v><C-h>d_<C-v><C-h>__<C-v><C-h>d_<C-v><C-h>l_<C-v><C-h>e<ESC>]]) + eval('man#init_pager()') + + screen:expect([[ + {b:^_begins} | + {b:mid_dle} | + {u:mid_dle} | + ~ | + | + ]]) + end) + + it('highlights various bullet formats', function() + rawfeed([[ + i· ·<C-v><C-h>· + +<C-v><C-h>o + +<C-v><C-h>+<C-v><C-h>o<C-v><C-h>o double<ESC>]]) + eval('man#init_pager()') + + screen:expect([[ + ^· {b:·} | + {b:·} | + {b:·} double | + ~ | + | + ]]) + end) + end) +end) diff --git a/test/functional/plugin/msgpack_spec.lua b/test/functional/plugin/msgpack_spec.lua index 5ba19708cf..4b014cbc73 100644 --- a/test/functional/plugin/msgpack_spec.lua +++ b/test/functional/plugin/msgpack_spec.lua @@ -8,7 +8,7 @@ local NIL = helpers.NIL local plugin_helpers = require('test.functional.plugin.helpers') local reset = plugin_helpers.reset -describe('In autoload/msgpack.vim', function() +describe('autoload/msgpack.vim', function() before_each(reset) local sp = function(typ, val) diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index dbc78e63f0..5a5b4df1ef 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -3,6 +3,7 @@ local eq, nvim_eval, nvim_command, nvim, exc_exec, funcs, nvim_feed, curbuf = helpers.eq, helpers.eval, helpers.command, helpers.nvim, helpers.exc_exec, helpers.funcs, helpers.feed, helpers.curbuf local neq = helpers.neq +local read_file = helpers.read_file local mpack = require('mpack') @@ -43,9 +44,7 @@ local wshada, _, fname = get_shada_rw('Xtest-functional-plugin-shada.shada') local wshada_tmp, _, fname_tmp = get_shada_rw('Xtest-functional-plugin-shada.shada.tmp.f') -if helpers.pending_win32(pending) then return end - -describe('In autoload/shada.vim', function() +describe('autoload/shada.vim', function() local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) before_each(function() reset() @@ -2138,8 +2137,9 @@ describe('In autoload/shada.vim', function() end) end) -describe('In plugin/shada.vim', function() +describe('plugin/shada.vim', function() local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) + local eol = helpers.iswin() and '\r\n' or '\n' before_each(function() reset() os.remove(fname) @@ -2153,9 +2153,7 @@ describe('In plugin/shada.vim', function() end) local shada_eq = function(expected, fname_) - local fd = io.open(fname_) - local mpack_result = fd:read('*a') - fd:close() + local mpack_result = read_file(fname_) mpack_eq(expected, mpack_result) end @@ -2279,7 +2277,7 @@ describe('In plugin/shada.vim', function() ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', - }, '\n') .. '\n', io.open(fname .. '.tst'):read('*a')) + }, eol) .. eol, read_file(fname .. '.tst')) shada_eq({{ timestamp=0, type=8, @@ -2303,6 +2301,7 @@ describe('In plugin/shada.vim', function() describe('event FileWriteCmd', function() it('works', function() + if helpers.pending_win32(pending) then return end nvim('set_var', 'shada#add_own_header', 0) curbuf('set_lines', 0, 1, true, { 'Jump with timestamp ' .. epoch .. ':', @@ -2326,7 +2325,7 @@ describe('In plugin/shada.vim', function() 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + n name \'A\'', - }, '\n') .. '\n', io.open(fname .. '.tst'):read('*a')) + }, eol) .. eol, read_file(fname .. '.tst')) shada_eq({{ timestamp=0, type=8, @@ -2383,7 +2382,7 @@ describe('In plugin/shada.vim', function() ' + f file name ["foo"]', ' + l line number 2', ' + c column -200', - }, '\n') .. '\n', io.open(fname .. '.tst'):read('*a')) + }, eol) .. eol, read_file(fname .. '.tst')) shada_eq({{ timestamp=0, type=8, diff --git a/test/functional/provider/nodejs_spec.lua b/test/functional/provider/nodejs_spec.lua new file mode 100644 index 0000000000..0a12b1a154 --- /dev/null +++ b/test/functional/provider/nodejs_spec.lua @@ -0,0 +1,61 @@ +local helpers = require('test.functional.helpers')(after_each) +local eq, clear = helpers.eq, helpers.clear +local missing_provider = helpers.missing_provider +local command = helpers.command +local write_file = helpers.write_file +local eval = helpers.eval +local retry = helpers.retry + +do + clear() + if missing_provider('node') then + pending("Missing nodejs host, or nodejs version is too old.", function()end) + return + end +end + +before_each(function() + clear() + command([[let $NODE_PATH = get(split(system('npm root -g'), "\n"), 0, '')]]) +end) + +describe('nodejs host', function() + teardown(function () + os.remove('Xtest-nodejs-hello.js') + os.remove('Xtest-nodejs-hello-plugin.js') + end) + + it('works', function() + local fname = 'Xtest-nodejs-hello.js' + write_file(fname, [[ + const socket = process.env.NVIM_LISTEN_ADDRESS; + const neovim = require('neovim'); + const nvim = neovim.attach({socket: socket}); + nvim.command('let g:job_out = "hello"'); + nvim.command('call jobstop(g:job_id)'); + ]]) + command('let g:job_id = jobstart(["node", "'..fname..'"])') + retry(nil, 1000, function() eq('hello', eval('g:job_out')) end) + end) + it('plugin works', function() + local fname = 'Xtest-nodejs-hello-plugin.js' + write_file(fname, [[ + const socket = process.env.NVIM_LISTEN_ADDRESS; + const neovim = require('neovim'); + const nvim = neovim.attach({socket: socket}); + + class TestPlugin { + hello() { + this.nvim.command('let g:job_out = "hello-plugin"') + } + } + + const PluginClass = neovim.Plugin(TestPlugin); + const plugin = new PluginClass(nvim); + plugin.hello(); + nvim.command('call jobstop(g:job_id)'); + ]]) + command('let g:job_id = jobstart(["node", "'..fname..'"])') + retry(nil, 1000, function() eq('hello-plugin', eval('g:job_out')) end) + end) +end) diff --git a/test/functional/provider/python3_spec.lua b/test/functional/provider/python3_spec.lua index aa50f53451..93ac3ae017 100644 --- a/test/functional/provider/python3_spec.lua +++ b/test/functional/provider/python3_spec.lua @@ -3,20 +3,18 @@ local eval, command, feed = helpers.eval, helpers.command, helpers.feed local eq, clear, insert = helpers.eq, helpers.clear, helpers.insert local expect, write_file = helpers.expect, helpers.write_file local feed_command = helpers.feed_command +local source = helpers.source local missing_provider = helpers.missing_provider do clear() - local err = missing_provider('python3') - if err then - pending( - 'Python 3 (or the Python 3 neovim module) is broken or missing:\n' .. err, - function() end) + if missing_provider('python3') then + pending('Python 3 (or the neovim module) is broken/missing', function() end) return end end -describe('python3 commands and functions', function() +describe('python3 provider', function() before_each(function() clear() command('python3 import vim') @@ -85,4 +83,20 @@ describe('python3 commands and functions', function() it('py3eval', function() eq({1, 2, {['key'] = 'val'}}, eval([[py3eval('[1, 2, {"key": "val"}]')]])) end) + + it('RPC call to expand("<afile>") during BufDelete #5245 #5617', function() + source([=[ + python3 << EOF + import vim + def foo(): + vim.eval('expand("<afile>:p")') + vim.eval('bufnr(expand("<afile>:p"))') + EOF + autocmd BufDelete * python3 foo() + autocmd BufUnload * python3 foo()]=]) + feed_command("exe 'split' tempname()") + feed_command("bwipeout!") + feed_command('help help') + eq(2, eval('1+1')) -- Still alive? + end) end) diff --git a/test/functional/provider/python_spec.lua b/test/functional/provider/python_spec.lua index 25f5e0a6d0..2fa74e9644 100644 --- a/test/functional/provider/python_spec.lua +++ b/test/functional/provider/python_spec.lua @@ -16,11 +16,8 @@ local missing_provider = helpers.missing_provider do clear() - local err = missing_provider('python') - if err then - pending( - 'Python 2 (or the Python 2 neovim module) is broken or missing:\n' .. err, - function() end) + if missing_provider('python') then + pending('Python 2 (or the neovim module) is broken/missing', function() end) return end end diff --git a/test/functional/provider/ruby_spec.lua b/test/functional/provider/ruby_spec.lua index c70f90da1c..e049ac7c41 100644 --- a/test/functional/provider/ruby_spec.lua +++ b/test/functional/provider/ruby_spec.lua @@ -1,23 +1,23 @@ local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local command = helpers.command +local curbufmeths = helpers.curbufmeths local eq = helpers.eq +local eval = helpers.eval +local expect = helpers.expect local feed = helpers.feed -local clear = helpers.clear +local feed_command = helpers.feed_command local funcs = helpers.funcs -local meths = helpers.meths local insert = helpers.insert -local expect = helpers.expect -local command = helpers.command -local write_file = helpers.write_file -local curbufmeths = helpers.curbufmeths +local meths = helpers.meths local missing_provider = helpers.missing_provider +local write_file = helpers.write_file do clear() if missing_provider('ruby') then - pending( - "Cannot find the neovim RubyGem. Try :checkhealth", - function() end) + pending("Missing neovim RubyGem.", function() end) return end end @@ -92,3 +92,11 @@ describe(':rubydo command', function() eq(false, curbufmeths.get_option('modified')) end) end) + +describe('ruby provider', function() + it('RPC call to expand("<afile>") during BufDelete #5245 #5617', function() + command([=[autocmd BufDelete * ruby VIM::evaluate('expand("<afile>")')]=]) + feed_command('help help') + eq(2, eval('1+1')) -- Still alive? + end) +end) diff --git a/test/functional/shada/helpers.lua b/test/functional/shada/helpers.lua index 8e2c0cc1f6..1312d762d8 100644 --- a/test/functional/shada/helpers.lua +++ b/test/functional/shada/helpers.lua @@ -6,15 +6,14 @@ local write_file, merge_args = helpers.write_file, helpers.merge_args local mpack = require('mpack') local tmpname = helpers.tmpname() -local additional_cmd = '' +local append_argv = nil -local function nvim_argv(shada_file) +local function nvim_argv(shada_file, embed) local argv = {nvim_prog, '-u', 'NONE', '-i', shada_file or tmpname, '-N', '--cmd', 'set shortmess+=I background=light noswapfile', - '--cmd', additional_cmd, - '--embed'} - if helpers.prepend_argv then - return merge_args(helpers.prepend_argv, argv) + embed or '--embed'} + if helpers.prepend_argv or append_argv then + return merge_args(helpers.prepend_argv, argv, append_argv) else return argv end @@ -26,12 +25,20 @@ local reset = function(shada_file) end local set_additional_cmd = function(s) - additional_cmd = s + append_argv = {'--cmd', s} +end + +local function add_argv(...) + if select('#', ...) == 0 then + append_argv = nil + else + append_argv = {...} + end end local clear = function() os.remove(tmpname) - set_additional_cmd('') + append_argv = nil end local get_shada_rw = function(fname) @@ -80,7 +87,9 @@ end return { reset=reset, set_additional_cmd=set_additional_cmd, + add_argv=add_argv, clear=clear, get_shada_rw=get_shada_rw, read_shada_file=read_shada_file, + nvim_argv=nvim_argv, } diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua index fa760ceb5b..4cceae1aa3 100644 --- a/test/functional/shada/marks_spec.lua +++ b/test/functional/shada/marks_spec.lua @@ -9,6 +9,8 @@ local shada_helpers = require('test.functional.shada.helpers') local reset, set_additional_cmd, clear = shada_helpers.reset, shada_helpers.set_additional_cmd, shada_helpers.clear +local add_argv = shada_helpers.add_argv +local nvim_argv = shada_helpers.nvim_argv local nvim_current_line = function() return curwinmeths.get_cursor()[1] @@ -17,8 +19,10 @@ end describe('ShaDa support code', function() local testfilename = 'Xtestfile-functional-shada-marks' local testfilename_2 = 'Xtestfile-functional-shada-marks-2' + local non_existent_testfilename = testfilename .. '.nonexistent' before_each(function() reset() + os.remove(non_existent_testfilename) local fd = io.open(testfilename, 'w') fd:write('test\n') fd:write('test2\n') @@ -214,4 +218,23 @@ describe('ShaDa support code', function() nvim_command('" sync 2') eq(2, nvim_current_line()) end) + + -- -c temporary sets lnum to zero to make `+/pat` work, so calling setpcmark() + -- during -c used to add item with zero lnum to jump list. + it('does not create incorrect file for non-existent buffers when writing from -c', + function() + add_argv('--cmd', 'silent edit ' .. non_existent_testfilename, '-c', 'qall') + local argv = nvim_argv(nil, '--headless') + eq('', funcs.system(argv)) + eq(0, exc_exec('rshada')) + end) + + it('does not create incorrect file for non-existent buffers opened from -c', + function() + add_argv('-c', 'silent edit ' .. non_existent_testfilename, + '-c', 'autocmd VimEnter * qall') + local argv = nvim_argv(nil, '--headless') + eq('', funcs.system(argv)) + eq(0, exc_exec('rshada')) + end) end) diff --git a/test/functional/shada/merging_spec.lua b/test/functional/shada/merging_spec.lua index 7a15c8908b..a628baff53 100644 --- a/test/functional/shada/merging_spec.lua +++ b/test/functional/shada/merging_spec.lua @@ -525,6 +525,85 @@ describe('ShaDa marks support code', function() eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) end) + it('can merge with file with mark 9 as the only numeric mark', function() + wshada('\007\001\014\130\161f\196\006' .. mock_file_path .. '-\161n9') + eq(0, exc_exec(sdrcmd())) + nvim_command('normal! `9oabc') + eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = {} + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 7 and v.value.f == mock_file_path .. '-' then + local name = ('%c'):format(v.value.n) + found[name] = (found[name] or 0) + 1 + end + end + eq({['0']=1, ['1']=1}, found) + end) + + it('removes duplicates while merging', function() + wshada('\007\001\014\130\161f\196\006' .. mock_file_path .. '-\161n9' + .. '\007\001\014\130\161f\196\006' .. mock_file_path .. '-\161n9') + eq(0, exc_exec(sdrcmd())) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = 0 + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 7 and v.value.f == mock_file_path .. '-' then + print(require('test.helpers').format_luav(v)) + found = found + 1 + end + end + eq(1, found) + end) + + it('does not leak when no append is performed due to too many marks', + function() + wshada('\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'a\161n0' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'b\161n1' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'c\161n2' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'd\161n3' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'e\161n4' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'f\161n5' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'g\161n6' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'h\161n7' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'i\161n8' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'j\161n9' + .. '\007\001\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'k\161n9') + eq(0, exc_exec(sdrcmd())) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = {} + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 7 and v.value.f:sub(1, #mock_file_path) == mock_file_path then + found[#found + 1] = v.value.f:sub(#v.value.f) + end + end + eq({'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}, found) + end) + + it('does not leak when last mark in file removes some of the earlier ones', + function() + wshada('\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'a\161n0' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'b\161n1' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'c\161n2' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'd\161n3' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'e\161n4' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'f\161n5' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'g\161n6' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'h\161n7' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'i\161n8' + .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'j\161n9' + .. '\007\003\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'k\161n9') + eq(0, exc_exec(sdrcmd())) + eq(0, exc_exec('wshada ' .. shada_fname)) + local found = {} + for _, v in ipairs(read_shada_file(shada_fname)) do + if v.type == 7 and v.value.f:sub(1, #mock_file_path) == mock_file_path then + found[#found + 1] = v.value.f:sub(#v.value.f) + end + end + eq({'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k'}, found) + end) + it('uses last A mark with gt timestamp from file when reading with !', function() wshada('\007\001\018\131\162mX\195\161f\196\006' .. mock_file_path .. '-\161nA') @@ -563,13 +642,14 @@ describe('ShaDa marks support code', function() nvim_command('normal! `A') eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) eq(0, exc_exec('wshada ' .. shada_fname)) - local found = 0 + local found = {} for _, v in ipairs(read_shada_file(shada_fname)) do - if v.type == 7 and v.value.f == '' .. mock_file_path .. '-' then - found = found + 1 + if v.type == 7 and v.value.f == mock_file_path .. '-' then + local name = ('%c'):format(v.value.n) + found[name] = (found[name] or 0) + 1 end end - eq(1, found) + eq({['0']=1, A=1}, found) end) it('uses last A mark with eq timestamp from instance when writing', @@ -580,30 +660,33 @@ describe('ShaDa marks support code', function() nvim_command('normal! `A') eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) eq(0, exc_exec('wshada ' .. shada_fname)) - local found = 0 + local found = {} for _, v in ipairs(read_shada_file(shada_fname)) do if v.type == 7 and v.value.f == mock_file_path .. '-' then - found = found + 1 + local name = ('%c'):format(v.value.n) + found[name] = (found[name] or 0) + 1 end end - eq(1, found) + eq({['0']=1, A=1}, found) end) - it('uses last A mark with gt timestamp from file when writing', - function() + it('uses last A mark with gt timestamp from file when writing', function() wshada('\007\001\018\131\162mX\195\161f\196\006' .. mock_file_path .. '-\161nA') eq(0, exc_exec(sdrcmd())) wshada('\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. '?\161nA') nvim_command('normal! `A') eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t')) eq(0, exc_exec('wshada ' .. shada_fname)) - local found = 0 + local found = {} for _, v in ipairs(read_shada_file(shada_fname)) do - if v.type == 7 and v.value.f == '' .. mock_file_path .. '?' then - found = found + 1 + if v.type == 7 then + local name = ('%c'):format(v.value.n) + local t = found[name] or {} + t[v.value.f] = (t[v.value.f] or 0) + 1 + found[name] = t end end - eq(1, found) + eq({['0']={[mock_file_path .. '-']=1}, A={[mock_file_path .. '?']=1}}, found) end) it('uses last a mark with gt timestamp from instance when reading', diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua index ca44026852..720855860a 100644 --- a/test/functional/shada/shada_spec.lua +++ b/test/functional/shada/shada_spec.lua @@ -181,13 +181,13 @@ describe('ShaDa support code', function() nvim_command('set shada+=%') nvim_command('wshada! ' .. shada_fname) local readme_fname = funcs.resolve(paths.test_source_path) .. '/README.md' - eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) + eq({[7]=2, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) nvim_command('set shada+=r~') nvim_command('wshada! ' .. shada_fname) eq({}, find_file(readme_fname)) nvim_command('set shada-=r~') nvim_command('wshada! ' .. shada_fname) - eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) + eq({[7]=2, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) nvim_command('set shada+=r' .. funcs.escape( funcs.escape(paths.test_source_path, '$~'), ' "\\,')) nvim_command('wshada! ' .. shada_fname) @@ -206,7 +206,7 @@ describe('ShaDa support code', function() nvim_command('undo') nvim_command('set shada+=%') nvim_command('wshada! ' .. shada_fname) - eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=2}, find_file(fname)) + eq({[7]=2, [8]=2, [9]=1, [10]=4, [11]=2}, find_file(fname)) nvim_command('set shada+=r' .. pwd .. '/АБВ') nvim_command('wshada! ' .. shada_fname) eq({}, find_file(fname)) diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua index d2b2d8a60c..84d7ae6e9c 100644 --- a/test/functional/terminal/edit_spec.lua +++ b/test/functional/terminal/edit_spec.lua @@ -36,6 +36,7 @@ describe(':edit term://*', function() local scr = get_screen(columns, lines) local rep = 'a' meths.set_option('shellcmdflag', 'REP ' .. rep) + command('set shellxquote=') -- win: avoid extra quotes local rep_size = rep:byte() -- 'a' => 97 local sb = 10 command('autocmd TermOpen * :setlocal scrollback='..tostring(sb) diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index e015df10db..4f22f7385d 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -2,12 +2,15 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, wait, nvim = helpers.clear, helpers.wait, helpers.nvim local nvim_dir, source, eq = helpers.nvim_dir, helpers.source, helpers.eq +local feed = helpers.feed local feed_command, eval = helpers.feed_command, helpers.eval +local funcs = helpers.funcs local retry = helpers.retry +local ok = helpers.ok local iswin = helpers.iswin +local command = helpers.command describe(':terminal', function() - if helpers.pending_win32(pending) then return end local screen before_each(function() @@ -24,10 +27,17 @@ describe(':terminal', function() echomsg "msg3" ]]) -- Invoke a command that emits frequent terminal activity. - feed_command([[terminal while true; do echo X; done]]) - helpers.feed([[<C-\><C-N>]]) + if iswin() then + feed_command([[terminal for /L \%I in (1,0,2) do echo \%I]]) + else + feed_command([[terminal while true; do echo X; done]]) + end + feed([[<C-\><C-N>]]) wait() - screen:sleep(10) -- Let some terminal activity happen. + -- Wait for some terminal activity. + retry(nil, 4000, function() + ok(funcs.line('$') > 6) + end) feed_command("messages") screen:expect([[ msg1 | @@ -38,8 +48,12 @@ describe(':terminal', function() end) it("in normal-mode :split does not move cursor", function() - feed_command([[terminal while true; do echo foo; sleep .1; done]]) - helpers.feed([[<C-\><C-N>M]]) -- move cursor away from last line + if iswin() then + feed_command([[terminal for /L \\%I in (1,0,2) do ( echo foo & ping -w 100 -n 1 127.0.0.1 > nul )]]) + else + feed_command([[terminal while true; do echo foo; sleep .1; done]]) + end + feed([[<C-\><C-N>M]]) -- move cursor away from last line wait() eq(3, eval("line('$')")) -- window height eq(2, eval("line('.')")) -- cursor is in the middle @@ -49,6 +63,32 @@ describe(':terminal', function() eq(2, eval("line('.')")) -- cursor stays where we put it end) + it('Enter/Leave does not increment jumplist #3723', function() + feed_command('terminal') + local function enter_and_leave() + local lines_before = funcs.line('$') + -- Create a new line (in the shell). For a normal buffer this + -- increments the jumplist; for a terminal-buffer it should not. #3723 + feed('i') + wait() + feed('<CR><CR><CR><CR>') + wait() + feed([[<C-\><C-N>]]) + wait() + -- Wait for >=1 lines to be created. + retry(nil, 4000, function() + ok(funcs.line('$') > lines_before) + end) + end + enter_and_leave() + enter_and_leave() + enter_and_leave() + ok(funcs.line('$') > 6) -- Verify assumption. + local jumps = funcs.split(funcs.execute('jumps'), '\n') + eq(' jump line col file/text', jumps[1]) + eq(3, #jumps) + end) + end) describe(':terminal (with fake shell)', function() @@ -104,6 +144,7 @@ describe(':terminal (with fake shell)', function() end) it('executes a given command through the shell', function() + command('set shellxquote=') -- win: avoid extra quotes terminal_with_fake_shell('echo hi') screen:expect([[ ^ready $ echo hi | @@ -115,6 +156,7 @@ describe(':terminal (with fake shell)', function() it("executes a given command through the shell, when 'shell' has arguments", function() nvim('set_option', 'shell', nvim_dir..'/shell-test -t jeff') + command('set shellxquote=') -- win: avoid extra quotes terminal_with_fake_shell('echo hi') screen:expect([[ ^jeff $ echo hi | @@ -125,6 +167,7 @@ describe(':terminal (with fake shell)', function() end) it('allows quotes and slashes', function() + command('set shellxquote=') -- win: avoid extra quotes terminal_with_fake_shell([[echo 'hello' \ "world"]]) screen:expect([[ ^ready $ echo 'hello' \ "world" | @@ -144,12 +187,12 @@ describe(':terminal (with fake shell)', function() it('ignores writes if the backing stream closes', function() terminal_with_fake_shell() - helpers.feed('iiXXXXXXX') + feed('iiXXXXXXX') wait() -- Race: Though the shell exited (and streams were closed by SIGCHLD -- handler), :terminal cleanup is pending on the main-loop. -- This write should be ignored (not crash, #5445). - helpers.feed('iiYYYYYYY') + feed('iiYYYYYYY') eq(2, eval("1+1")) -- Still alive? end) @@ -168,7 +211,7 @@ describe(':terminal (with fake shell)', function() :terminal | ]]) eq('term://', string.match(eval('bufname("%")'), "^term://")) - helpers.feed([[<C-\><C-N>]]) + feed([[<C-\><C-N>]]) feed_command([[find */shadacat.py]]) if iswin() then eq('scripts\\shadacat.py', eval('bufname("%")')) @@ -178,6 +221,7 @@ describe(':terminal (with fake shell)', function() end) it('works with gf', function() + command('set shellxquote=') -- win: avoid extra quotes terminal_with_fake_shell([[echo "scripts/shadacat.py"]]) screen:expect([[ ^ready $ echo "scripts/shadacat.py" | @@ -185,9 +229,9 @@ describe(':terminal (with fake shell)', function() [Process exited 0] | :terminal echo "scripts/shadacat.py" | ]]) - helpers.feed([[<C-\><C-N>]]) + feed([[<C-\><C-N>]]) eq('term://', string.match(eval('bufname("%")'), "^term://")) - helpers.feed([[ggf"lgf]]) + feed([[ggf"lgf]]) eq('scripts/shadacat.py', eval('bufname("%")')) end) diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua index 5e5558ee0a..a21d9f0a56 100644 --- a/test/functional/terminal/mouse_spec.lua +++ b/test/functional/terminal/mouse_spec.lua @@ -98,41 +98,41 @@ describe('terminal mouse', function() before_each(function() feed('<c-\\><c-n>:vsp<cr>') screen:expect([[ - line28 |line28 | - line29 |line29 | - line30 |line30 | - rows: 5, cols: 24 |rows: 5, cols: 24 | - {2:^ } |{2: } | + line28 │line28 | + line29 │line29 | + line30 │line30 | + rows: 5, cols: 24 │rows: 5, cols: 24 | + {2:^ } │{2: } | ========== ========== | :vsp | ]]) feed(':enew | set number<cr>') screen:expect([[ - {7: 1 }^ |line28 | - {4:~ }|line29 | - {4:~ }|line30 | - {4:~ }|rows: 5, cols: 24 | - {4:~ }|{2: } | + {7: 1 }^ │line28 | + {4:~ }│line29 | + {4:~ }│line30 | + {4:~ }│rows: 5, cols: 24 | + {4:~ }│{2: } | ========== ========== | :enew | set number | ]]) feed('30iline\n<esc>') screen:expect([[ - {7: 27 }line |line28 | - {7: 28 }line |line29 | - {7: 29 }line |line30 | - {7: 30 }line |rows: 5, cols: 24 | - {7: 31 }^ |{2: } | + {7: 27 }line │line28 | + {7: 28 }line │line29 | + {7: 29 }line │line30 | + {7: 30 }line │rows: 5, cols: 24 | + {7: 31 }^ │{2: } | ========== ========== | | ]]) feed('<c-w>li') screen:expect([[ - {7: 27 }line |line28 | - {7: 28 }line |line29 | - {7: 29 }line |line30 | - {7: 30 }line |rows: 5, cols: 24 | - {7: 31 } |{1: } | + {7: 27 }line │line28 | + {7: 28 }line │line29 | + {7: 29 }line │line30 | + {7: 30 }line │rows: 5, cols: 24 | + {7: 31 } │{1: } | ========== ========== | {3:-- TERMINAL --} | ]]) @@ -140,11 +140,11 @@ describe('terminal mouse', function() thelpers.enable_mouse() thelpers.feed_data('mouse enabled\n') screen:expect([[ - {7: 27 }line |line29 | - {7: 28 }line |line30 | - {7: 29 }line |rows: 5, cols: 24 | - {7: 30 }line |mouse enabled | - {7: 31 } |{1: } | + {7: 27 }line │line29 | + {7: 28 }line │line30 | + {7: 29 }line │rows: 5, cols: 24 | + {7: 30 }line │mouse enabled | + {7: 31 } │{1: } | ========== ========== | {3:-- TERMINAL --} | ]]) @@ -153,21 +153,21 @@ describe('terminal mouse', function() it('wont lose focus if another window is scrolled', function() feed('<ScrollWheelUp><0,0><ScrollWheelUp><0,0>') screen:expect([[ - {7: 21 }line |line29 | - {7: 22 }line |line30 | - {7: 23 }line |rows: 5, cols: 24 | - {7: 24 }line |mouse enabled | - {7: 25 }line |{1: } | + {7: 21 }line │line29 | + {7: 22 }line │line30 | + {7: 23 }line │rows: 5, cols: 24 | + {7: 24 }line │mouse enabled | + {7: 25 }line │{1: } | ========== ========== | {3:-- TERMINAL --} | ]]) feed('<S-ScrollWheelDown><0,0>') screen:expect([[ - {7: 26 }line |line29 | - {7: 27 }line |line30 | - {7: 28 }line |rows: 5, cols: 24 | - {7: 29 }line |mouse enabled | - {7: 30 }line |{1: } | + {7: 26 }line │line29 | + {7: 27 }line │line30 | + {7: 28 }line │rows: 5, cols: 24 | + {7: 29 }line │mouse enabled | + {7: 30 }line │{1: } | ========== ========== | {3:-- TERMINAL --} | ]]) @@ -176,11 +176,11 @@ describe('terminal mouse', function() it('will lose focus if another window is clicked', function() feed('<LeftMouse><5,1>') screen:expect([[ - {7: 27 }line |line29 | - {7: 28 }l^ine |line30 | - {7: 29 }line |rows: 5, cols: 24 | - {7: 30 }line |mouse enabled | - {7: 31 } |{2: } | + {7: 27 }line │line29 | + {7: 28 }l^ine │line30 | + {7: 29 }line │rows: 5, cols: 24 | + {7: 30 }line │mouse enabled | + {7: 31 } │{2: } | ========== ========== | | ]]) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index d5f6a21d1d..2f9abfd3f6 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -4,13 +4,20 @@ local global_helpers = require('test.helpers') local uname = global_helpers.uname local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') -local feed_data = thelpers.feed_data +local Screen = require('test.functional.ui.screen') +local eq = helpers.eq local feed_command = helpers.feed_command +local feed_data = thelpers.feed_data local clear = helpers.clear +local command = helpers.command +local eval = helpers.eval local nvim_dir = helpers.nvim_dir local retry = helpers.retry local nvim_prog = helpers.nvim_prog local nvim_set = helpers.nvim_set +local ok = helpers.ok +local read_file = helpers.read_file +local wait = helpers.wait if helpers.pending_win32(pending) then return end @@ -21,9 +28,6 @@ describe('tui', function() clear() screen = thelpers.screen_setup(0, '["'..nvim_prog ..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler undodir=. directory=. viewdir=. backupdir=."]') - -- right now pasting can be really slow in the TUI, especially in ASAN. - -- this will be fixed later but for now we require a high timeout. - screen.timeout = 60000 screen:expect([[ {1: } | {4:~ }| @@ -39,6 +43,28 @@ describe('tui', function() screen:detach() end) + it('rapid resize #7572 #7628', function() + -- Need buffer rows to provoke the behavior. + feed_data(":edit test/functional/fixtures/bigfile.txt:") + command('call jobresize(b:terminal_job_id, 58, 9)') + command('call jobresize(b:terminal_job_id, 62, 13)') + command('call jobresize(b:terminal_job_id, 100, 42)') + command('call jobresize(b:terminal_job_id, 37, 1000)') + -- Resize to <5 columns. + screen:try_resize(4, 44) + command('call jobresize(b:terminal_job_id, 4, 1000)') + -- Resize to 1 row, then to 1 column, then increase rows to 4. + screen:try_resize(44, 1) + command('call jobresize(b:terminal_job_id, 44, 1)') + screen:try_resize(1, 1) + command('call jobresize(b:terminal_job_id, 1, 1)') + screen:try_resize(1, 4) + command('call jobresize(b:terminal_job_id, 1, 4)') + screen:try_resize(57, 17) + command('call jobresize(b:terminal_job_id, 57, 17)') + eq(2, eval("1+1")) -- Still alive? + end) + it('accepts basic utf-8 input', function() feed_data('iabc\ntest1\ntest2') screen:expect([[ @@ -89,16 +115,12 @@ describe('tui', function() ]]) end) - it('does not mangle unmapped ALT-key chord', function() - -- Vim represents ALT/META by setting the "high bit" of the modified key; - -- we do _not_. #3982 - -- - -- Example: for input ALT+j: - -- * Vim (Nvim prior to #3982) sets high-bit, inserts "ê". - -- * Nvim (after #3982) inserts "j". - feed_data('i\027j') + it('interprets ESC+key as ALT chord', function() + -- Vim represents ALT/META by setting the "high bit" of the modified key: + -- ALT+j inserts "ê". Nvim does not (#3982). + feed_data('i\022\027j') screen:expect([[ - j{1: } | + <M-j>{1: } | {4:~ }| {4:~ }| {4:~ }| @@ -125,6 +147,9 @@ describe('tui', function() end) it('automatically sends <Paste> for bracketed paste sequences', function() + -- Pasting can be really slow in the TUI, specially in ASAN. + -- This will be fixed later but for now we require a high timeout. + screen.timeout = 60000 feed_data('i\027[200~') screen:expect([[ {1: } | @@ -158,6 +183,8 @@ describe('tui', function() end) it('can handle arbitrarily long bursts of input', function() + -- Need extra time for this test, specially in ASAN. + screen.timeout = 60000 feed_command('set ruler') local t = {} for i = 1, 3000 do @@ -174,6 +201,58 @@ describe('tui', function() {3:-- TERMINAL --} | ]]) end) + + it('allows termguicolors to be set at runtime', function() + screen:set_option('rgb', true) + screen:set_default_attr_ids({ + [1] = {reverse = true}, + [2] = {foreground = 13, special = Screen.colors.Grey0}, + [3] = {special = Screen.colors.Grey0, bold = true, reverse = true}, + [4] = {bold = true}, + [5] = {special = Screen.colors.Grey0, reverse = true, foreground = 4}, + [6] = {foreground = 4, special = Screen.colors.Grey0}, + [7] = {special = Screen.colors.Grey0, reverse = true, foreground = Screen.colors.SeaGreen4}, + [8] = {foreground = Screen.colors.SeaGreen4, special = Screen.colors.Grey0}, + [9] = {special = Screen.colors.Grey0, bold = true, foreground = Screen.colors.Blue1}, + }) + + feed_data(':hi SpecialKey ctermfg=3 guifg=SeaGreen\n') + feed_data('i') + feed_data('\022\007') -- ctrl+g + feed_data('\028\014') -- crtl+\ ctrl+N + feed_data(':set termguicolors?\n') + screen:expect([[ + {5:^}{6:G} | + {2:~ }| + {2:~ }| + {2:~ }| + {3:[No Name] [+] }| + notermguicolors | + {4:-- TERMINAL --} | + ]]) + + feed_data(':set termguicolors\n') + screen:expect([[ + {7:^}{8:G} | + {9:~ }| + {9:~ }| + {9:~ }| + {3:[No Name] [+] }| + :set termguicolors | + {4:-- TERMINAL --} | + ]]) + + feed_data(':set notermguicolors\n') + screen:expect([[ + {5:^}{6:G} | + {2:~ }| + {2:~ }| + {2:~ }| + {3:[No Name] [+] }| + :set notermguicolors | + {4:-- TERMINAL --} | + ]]) + end) end) describe('tui with non-tty file descriptors', function() @@ -390,7 +469,7 @@ describe("tui 't_Co' (terminal colors)", function() nvim_set)) feed_data(":echo &t_Co\n") - helpers.wait() + wait() local tline if maxcolors == 8 or maxcolors == 16 then tline = "~ " @@ -639,6 +718,7 @@ end) describe("tui 'term' option", function() local screen local is_bsd = not not string.find(string.lower(uname()), 'bsd') + local is_macos = not not string.find(string.lower(uname()), 'darwin') local function assert_term(term_envvar, term_expected) clear() @@ -664,11 +744,62 @@ describe("tui 'term' option", function() end) it('gets system-provided term if $TERM is valid', function() - if is_bsd then -- BSD lacks terminfo, we always use builtin there. + if is_bsd then -- BSD lacks terminfo, builtin is always used. assert_term("xterm", "builtin_xterm") + elseif is_macos then + local status, _ = pcall(assert_term, "xterm", "xterm") + if not status then + pending("macOS: unibilium could not find terminfo", function() end) + end else assert_term("xterm", "xterm") end end) end) + +-- These tests require `thelpers` because --headless/--embed +-- does not initialize the TUI. +describe("tui", function() + local screen + local logfile = 'Xtest_tui_verbose_log' + after_each(function() + os.remove(logfile) + end) + + -- Runs (child) `nvim` in a TTY (:terminal), to start the builtin TUI. + local function nvim_tui(extra_args) + clear() + -- This is ugly because :term/termopen() forces TERM=xterm-256color. + -- TODO: Revisit this after jobstart/termopen accept `env` dict. + local cmd = string.format( + [=[['sh', '-c', 'LANG=C %s -u NONE -i NONE %s --cmd "%s"']]=], + nvim_prog, + extra_args or "", + nvim_set) + screen = thelpers.screen_setup(0, cmd) + end + + it('-V3log logs terminfo values', function() + nvim_tui('-V3'..logfile) + + -- Wait for TUI to start. + feed_data('Gitext') + screen:expect([[ + text{1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {4:~ }| + {3:-- INSERT --} | + {3:-- TERMINAL --} | + ]]) + + retry(nil, 3000, function() -- Wait for log file to be flushed. + local log = read_file('Xtest_tui_verbose_log') or '' + eq('--- Terminal info --- {{{\n', string.match(log, '--- Terminal.-\n')) + ok(#log > 50) + end) + end) + +end) diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua index 2143c01139..5b38921e50 100644 --- a/test/functional/ui/bufhl_spec.lua +++ b/test/functional/ui/bufhl_spec.lua @@ -2,13 +2,11 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local command, request, neq = helpers.command, helpers.request, helpers.neq - -if helpers.pending_win32(pending) then return end +local command, neq = helpers.command, helpers.neq +local curbufmeths = helpers.curbufmeths describe('Buffer highlighting', function() local screen - local curbuf before_each(function() clear() @@ -27,21 +25,14 @@ describe('Buffer highlighting', function() [9] = {foreground = Screen.colors.SlateBlue, underline = true}, [10] = {foreground = Screen.colors.Red} }) - curbuf = request('nvim_get_current_buf') end) after_each(function() screen:detach() end) - local function add_hl(...) - return request('nvim_buf_add_highlight', curbuf, ...) - end - - local function clear_hl(...) - return request('nvim_buf_clear_highlight', curbuf, ...) - end - + local add_hl = curbufmeths.add_highlight + local clear_hl = curbufmeths.clear_highlight it('works', function() insert([[ diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index d87ce72599..3c316d1cfa 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -24,6 +24,7 @@ before_each(function() clear() screen = Screen.new(40, 8) screen:attach() + command("set display-=msgsep") source([[ highlight RBP1 guibg=Red highlight RBP2 guibg=Yellow @@ -144,7 +145,13 @@ before_each(function() EOB={bold = true, foreground = Screen.colors.Blue1}, ERR={foreground = Screen.colors.Grey100, background = Screen.colors.Red}, SK={foreground = Screen.colors.Blue}, - PE={bold = true, foreground = Screen.colors.SeaGreen4} + PE={bold = true, foreground = Screen.colors.SeaGreen4}, + NUM={foreground = Screen.colors.Blue2}, + NPAR={foreground = Screen.colors.Yellow}, + SQ={foreground = Screen.colors.Blue3}, + SB={foreground = Screen.colors.Blue4}, + E={foreground = Screen.colors.Red, background = Screen.colors.Blue}, + M={bold = true}, }) end) @@ -731,6 +738,22 @@ describe('Command-line coloring', function() feed('<CR><CR>') eq('', meths.get_var('out')) end) + it('does not crash when callback has caught not-a-editor-command exception', + function() + source([[ + function CaughtExc(cmdline) abort + try + gibberish + catch + " Do nothing + endtry + return [] + endfunction + ]]) + set_color_cb('CaughtExc') + start_prompt('1') + eq(1, meths.eval('1')) + end) end) describe('Ex commands coloring support', function() it('works', function() @@ -843,7 +866,7 @@ describe('Ex commands coloring support', function() {EOB:~ }| | ]]) - eq('\nError detected while processing :\nE605: Exception not caught: 42', + eq('Error detected while processing :\nE605: Exception not caught: 42', meths.command_output('messages')) end) it('errors out when failing to get callback', function() @@ -863,7 +886,10 @@ describe('Ex commands coloring support', function() end) describe('Expressions coloring support', function() it('works', function() - meths.set_var('Nvim_color_expr', 'RainBowParens') + meths.command('hi clear NvimNumber') + meths.command('hi clear NvimNestingParenthesis') + meths.command('hi NvimNumber guifg=Blue2') + meths.command('hi NvimNestingParenthesis guifg=Yellow') feed(':echo <C-r>=(((1)))') screen:expect([[ | @@ -873,21 +899,103 @@ describe('Expressions coloring support', function() {EOB:~ }| {EOB:~ }| {EOB:~ }| - ={RBP1:(}{RBP2:(}{RBP3:(}1{RBP3:)}{RBP2:)}{RBP1:)}^ | + ={NPAR:(((}{NUM:1}{NPAR:)))}^ | ]]) end) - it('errors out when failing to get callback', function() + it('does not use Nvim_color_expr', function() meths.set_var('Nvim_color_expr', 42) + -- Used to error out due to failing to get callback. + meths.command('hi clear NvimNumber') + meths.command('hi NvimNumber guifg=Blue2') feed(':<C-r>=1') screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + ={NUM:1}^ | + ]]) + end) + it('works correctly with non-ASCII and control characters', function() + meths.command('hi clear NvimStringBody') + meths.command('hi clear NvimStringQuote') + meths.command('hi clear NvimInvalid') + meths.command('hi NvimStringQuote guifg=Blue3') + meths.command('hi NvimStringBody guifg=Blue4') + meths.command('hi NvimInvalid guifg=Red guibg=Blue') + feed('i<C-r>="«»"«»') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + ={SQ:"}{SB:«»}{SQ:"}{E:«»}^ | + ]]) + feed('<C-c>') + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {M:-- INSERT --} | + ]]) + feed('<Esc>') + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + | + ]]) + feed(':<C-\\>e"<C-v><C-x>"<C-v><C-x>') + -- TODO(ZyX-I): Parser highlighting should not override special character + -- highlighting. + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + ={SQ:"}{SB:^X}{SQ:"}{ERR:^X}^ | + ]]) + feed('<C-c>') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :^ | + ]]) + funcs.setreg('a', {'\192'}) + feed('<C-r>="<C-r><C-r>a"<C-r><C-r>a"foo"') + -- TODO(ZyX-I): Parser highlighting should not override special character + -- highlighting. + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| {EOB:~ }| {EOB:~ }| {EOB:~ }| - = | - {ERR:E5409: Unable to get g:Nvim_color_expr c}| - {ERR:allback: Vim:E6000: Argument is not a fu}| - {ERR:nction or function name} | - =1^ | + ={SQ:"}{SB:<c0>}{SQ:"}{E:<c0>"}{SB:foo}{E:"}^ | ]]) end) end) diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index 0f8302b036..f8680678ef 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -10,12 +10,19 @@ describe('external cmdline', function() local last_level = 0 local cmdline = {} local block = nil + local wild_items = nil + local wild_selected = nil before_each(function() clear() cmdline, block = {}, nil screen = Screen.new(25, 5) screen:attach({rgb=true, ext_cmdline=true}) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {reverse = true}, + [3] = {bold = true, reverse = true}, + }) screen:set_on_event_handler(function(name, data) if name == "cmdline_show" then local content, pos, firstc, prompt, indent, level = unpack(data) @@ -38,6 +45,12 @@ describe('external cmdline', function() block[#block+1] = data[1] elseif name == "cmdline_block_hide" then block = nil + elseif name == "wildmenu_show" then + wild_items = data[1] + elseif name == "wildmenu_select" then + wild_selected = data[1] + elseif name == "wildmenu_hide" then + wild_items, wild_selected = nil, nil end end) end) @@ -66,9 +79,9 @@ describe('external cmdline', function() feed(':') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq(1, last_level) @@ -84,9 +97,9 @@ describe('external cmdline', function() feed('sign') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -101,9 +114,9 @@ describe('external cmdline', function() feed('<Left>') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -118,9 +131,9 @@ describe('external cmdline', function() feed('<bs>') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -135,9 +148,9 @@ describe('external cmdline', function() feed('<Esc>') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({}, cmdline) @@ -148,9 +161,9 @@ describe('external cmdline', function() feed(':call input("input", "default")<cr>') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -164,9 +177,9 @@ describe('external cmdline', function() feed('<cr>') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({}, cmdline) @@ -178,9 +191,9 @@ describe('external cmdline', function() feed(':xx<c-r>') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -196,9 +209,9 @@ describe('external cmdline', function() feed('=') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -226,7 +239,11 @@ describe('external cmdline', function() prompt = "", special = {'"', true}, },{ - content = { { {}, "1+2" } }, + content = { + { {}, "1" }, + { {}, "+" }, + { {}, "2" }, + }, firstc = "=", indent = 0, pos = 3, @@ -234,9 +251,9 @@ describe('external cmdline', function() }} screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq(expectation, cmdline) @@ -249,9 +266,9 @@ describe('external cmdline', function() -- focus is at external cmdline anyway. screen:expect([[ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| ^ | ]], nil, nil, function() eq(expectation, cmdline) @@ -261,9 +278,9 @@ describe('external cmdline', function() feed('<cr>') screen:expect([[ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| ^ | ]], nil, nil, function() eq({{ @@ -278,9 +295,9 @@ describe('external cmdline', function() feed('<esc>') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({}, cmdline) @@ -291,9 +308,9 @@ describe('external cmdline', function() feed(':function Foo()<cr>') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -303,41 +320,70 @@ describe('external cmdline', function() pos = 0, prompt = "", }}, cmdline) - eq({{{{}, 'function Foo()'}}}, block) + eq({ { { {}, 'function Foo()'} } }, block) end) feed('line1<cr>') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() - eq({{{{}, 'function Foo()'}}, - {{{}, ' line1'}}}, block) + eq({ { { {}, 'function Foo()'} }, + { { {}, ' line1'} } }, block) end) block = {} command("redraw!") screen:expect([[ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| ^ | ]], nil, nil, function() - eq({{{{}, 'function Foo()'}}, - {{{}, ' line1'}}}, block) + eq({ { { {}, 'function Foo()'} }, + { { {}, ' line1'} } }, block) end) + feed('endfunction<cr>') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], nil, nil, function() + eq(nil, block) + end) + + -- Try once more, to check buffer is reinitialized. #8007 + feed(':function Bar()<cr>') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], nil, nil, function() + eq({{ + content = { { {}, "" } }, + firstc = ":", + indent = 2, + pos = 0, + prompt = "", + }}, cmdline) + eq({ { { {}, 'function Bar()'} } }, block) + end) feed('endfunction<cr>') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq(nil, block) @@ -348,9 +394,9 @@ describe('external cmdline', function() feed(':make') screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -365,9 +411,9 @@ describe('external cmdline', function() feed('<c-f>') screen:expect([[ | - [No Name] | - :make^ | - [Command Line] | + {2:[No Name] }| + {1::}make^ | + {3:[Command Line] }| | ]], nil, nil, function() eq({}, cmdline) @@ -377,9 +423,9 @@ describe('external cmdline', function() feed(':yank') screen:expect([[ | - [No Name] | - :make^ | - [Command Line] | + {2:[No Name] }| + {1::}make^ | + {3:[Command Line] }| | ]], nil, nil, function() eq({nil, { @@ -395,9 +441,9 @@ describe('external cmdline', function() command("redraw!") screen:expect([[ | - [No Name] | - :make | - [Command Line] | + {2:[No Name] }| + {1::}make | + {3:[Command Line] }| ^ | ]], nil, nil, function() eq({nil, { @@ -412,9 +458,9 @@ describe('external cmdline', function() feed("<c-c>") screen:expect([[ | - [No Name] | - :make^ | - [Command Line] | + {2:[No Name] }| + {1::}make^ | + {3:[Command Line] }| | ]], nil, nil, function() eq({}, cmdline) @@ -423,9 +469,9 @@ describe('external cmdline', function() feed("<c-c>") screen:expect([[ | - [No Name] | - :make^ | - [Command Line] | + {2:[No Name] }| + {1::}make^ | + {3:[Command Line] }| | ]], nil, nil, function() eq({{ @@ -441,9 +487,9 @@ describe('external cmdline', function() command("redraw!") screen:expect([[ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| ^ | ]], nil, nil, function() eq({{ @@ -460,9 +506,9 @@ describe('external cmdline', function() feed(":call inputsecret('secret:')<cr>abc123") screen:expect([[ ^ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| | ]], nil, nil, function() eq({{ @@ -476,50 +522,160 @@ describe('external cmdline', function() end) it('works with highlighted cmdline', function() - source([[ - highlight RBP1 guibg=Red - highlight RBP2 guibg=Yellow - highlight RBP3 guibg=Green - highlight RBP4 guibg=Blue - let g:NUM_LVLS = 4 - 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 - map <f5> :let x = input({'prompt':'>','highlight':'RainBowParens'})<cr> - "map <f5> :let x = input({'prompt':'>'})<cr> - ]]) - screen:set_default_attr_ids({ - RBP1={background = Screen.colors.Red}, - RBP2={background = Screen.colors.Yellow}, - RBP3={background = Screen.colors.Green}, - RBP4={background = Screen.colors.Blue}, - EOB={bold = true, foreground = Screen.colors.Blue1}, - ERR={foreground = Screen.colors.Grey100, background = Screen.colors.Red}, - SK={foreground = Screen.colors.Blue}, - PE={bold = true, foreground = Screen.colors.SeaGreen4} - }) - feed('<f5>(a(b)a)') - screen:expect([[ - ^ | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - | - ]], nil, nil, function() - expect_cmdline(1, '{RBP1:(}a{RBP2:(}b{RBP2:)}a{RBP1:)}') - end) + source([[ + highlight RBP1 guibg=Red + highlight RBP2 guibg=Yellow + highlight RBP3 guibg=Green + highlight RBP4 guibg=Blue + let g:NUM_LVLS = 4 + 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 + map <f5> :let x = input({'prompt':'>','highlight':'RainBowParens'})<cr> + "map <f5> :let x = input({'prompt':'>'})<cr> + ]]) + screen:set_default_attr_ids({ + RBP1={background = Screen.colors.Red}, + RBP2={background = Screen.colors.Yellow}, + RBP3={background = Screen.colors.Green}, + RBP4={background = Screen.colors.Blue}, + EOB={bold = true, foreground = Screen.colors.Blue1}, + ERR={foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + SK={foreground = Screen.colors.Blue}, + PE={bold = true, foreground = Screen.colors.SeaGreen4} + }) + feed('<f5>(a(b)a)') + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + | + ]], nil, nil, function() + expect_cmdline(1, '{RBP1:(}a{RBP2:(}b{RBP2:)}a{RBP1:)}') + end) + end) + + it('works together with ext_wildmenu', function() + local expected = { + 'define', + 'jump', + 'list', + 'place', + 'undefine', + 'unplace', + } + + command('set wildmode=full') + command('set wildmenu') + screen:set_option('ext_wildmenu', true) + feed(':sign <tab>') + + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], nil, nil, function() + eq({{ + content = { { {}, "sign define"} }, + firstc = ":", + indent = 0, + pos = 11, + prompt = "" + }}, cmdline) + eq(expected, wild_items) + eq(0, wild_selected) + end) + + feed('<tab>') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], nil, nil, function() + eq({{ + content = { { {}, "sign jump"} }, + firstc = ":", + indent = 0, + pos = 9, + prompt = "" + }}, cmdline) + eq(expected, wild_items) + eq(1, wild_selected) + end) + + feed('<left><left>') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], nil, nil, function() + eq({{ + content = { { {}, "sign "} }, + firstc = ":", + indent = 0, + pos = 5, + prompt = "" + }}, cmdline) + eq(expected, wild_items) + eq(-1, wild_selected) + end) + + feed('<right>') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], nil, nil, function() + eq({{ + content = { { {}, "sign define"} }, + firstc = ":", + indent = 0, + pos = 11, + prompt = "" + }}, cmdline) + eq(expected, wild_items) + eq(0, wild_selected) + end) + + feed('a') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], nil, nil, function() + eq({{ + content = { { {}, "sign definea"} }, + firstc = ":", + indent = 0, + pos = 12, + prompt = "" + }}, cmdline) + eq(nil, wild_items) + eq(nil, wild_selected) + end) end) end) diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index b47210a777..812c095add 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -194,8 +194,8 @@ describe('ui/cursor', function() if m.blinkoff then m.blinkoff = 400 end if m.blinkwait then m.blinkwait = 700 end end - if m.hl_id then m.hl_id = 48 end - if m.id_lm then m.id_lm = 49 end + if m.hl_id then m.hl_id = 49 end + if m.id_lm then m.id_lm = 50 end end -- Assert the new expectation. diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 2252e3580f..ab3b1c3cac 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -94,6 +94,7 @@ describe('highlight defaults', function() clear() screen = Screen.new() screen:attach() + command("set display-=msgsep") end) after_each(function() @@ -108,12 +109,12 @@ describe('highlight defaults', function() }) feed_command('sp', 'vsp', 'vsp') screen:expect([[ - ^ {2:|} {2:|} | - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| + ^ {2:│} {2:│} | + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| {1:[No Name] }{2:[No Name] [No Name] }| | {0:~ }| @@ -126,12 +127,12 @@ describe('highlight defaults', function() -- navigate to verify that the attributes are properly moved feed('<c-w>j') screen:expect([[ - {2:|} {2:|} | - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| + {2:│} {2:│} | + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| {2:[No Name] [No Name] [No Name] }| ^ | {0:~ }| @@ -146,12 +147,12 @@ describe('highlight defaults', function() -- (upstream vim has the same behavior) feed('<c-w>k<c-w>l') screen:expect([[ - {2:|}^ {2:|} | - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| + {2:│}^ {2:│} | + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| {2:[No Name] }{1:[No Name] }{2:[No Name] }| | {0:~ }| @@ -163,12 +164,12 @@ describe('highlight defaults', function() ]]) feed('<c-w>l') screen:expect([[ - {2:|} {2:|}^ | - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| + {2:│} {2:│}^ | + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| {2:[No Name] [No Name] }{1:[No Name] }| | {0:~ }| @@ -180,12 +181,12 @@ describe('highlight defaults', function() ]]) feed('<c-w>h<c-w>h') screen:expect([[ - ^ {2:|} {2:|} | - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| - {0:~ }{2:|}{0:~ }{2:|}{0:~ }| + ^ {2:│} {2:│} | + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| + {0:~ }{2:│}{0:~ }{2:│}{0:~ }| {1:[No Name] }{2:[No Name] [No Name] }| | {0:~ }| @@ -312,7 +313,7 @@ describe('highlight defaults', function() end) end) -describe('guisp (special/undercurl)', function() +describe('highlight', function() local screen before_each(function() @@ -321,7 +322,31 @@ describe('guisp (special/undercurl)', function() screen:attach() end) - it('can be set and is applied like foreground or background', function() + it('cterm=standout gui=standout', function() + screen:detach() + screen = Screen.new(20,5) + screen:attach() + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {standout = true, bold = true, underline = true, + background = Screen.colors.Gray90, foreground = Screen.colors.Blue1}, + [3] = {standout = true, underline = true, + background = Screen.colors.Gray90} + }) + feed_command('hi CursorLine cterm=standout,underline gui=standout,underline') + feed_command('set cursorline') + feed_command('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list') + feed('i\t abcd <cr>\t abcd <cr><esc>k') + screen:expect([[ + {1:>-------.}abcd{1:*¬} | + {2:^>-------.}{3:abcd}{2:*¬}{3: }| + {1:¬} | + {1:~ }| + | + ]]) + end) + + it('guisp (special/undercurl)', function() feed_command('syntax on') feed_command('syn keyword TmpKeyword neovim') feed_command('syn keyword TmpKeyword1 special') @@ -650,6 +675,76 @@ describe("'listchars' highlight", function() end) end) +describe("MsgSeparator highlight and msgsep fillchar", function() + before_each(clear) + it("works", function() + local screen = Screen.new(50,5) + screen:set_default_attr_ids({ + [1] = {bold=true, foreground=Screen.colors.Blue}, + [2] = {bold=true, reverse=true}, + [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [4] = {background = Screen.colors.Cyan, bold = true, reverse = true}, + [5] = {bold = true, background = Screen.colors.Magenta} + }) + screen:attach() + + -- defaults + feed_command("ls") + screen:expect([[ + | + {2: }| + :ls | + 1 %a "[No Name]" line 1 | + {3:Press ENTER or type command to continue}^ | + ]]) + feed('<cr>') + + feed_command("set fillchars+=msgsep:-") + feed_command("ls") + screen:expect([[ + | + {2:--------------------------------------------------}| + :ls | + 1 %a "[No Name]" line 1 | + {3:Press ENTER or type command to continue}^ | + ]]) + + -- linked to StatusLine per default + feed_command("hi StatusLine guibg=Cyan") + feed_command("ls") + screen:expect([[ + | + {4:--------------------------------------------------}| + :ls | + 1 %a "[No Name]" line 1 | + {3:Press ENTER or type command to continue}^ | + ]]) + + -- but can be unlinked + feed_command("hi clear MsgSeparator") + feed_command("hi MsgSeparator guibg=Magenta gui=bold") + feed_command("ls") + screen:expect([[ + | + {5:--------------------------------------------------}| + :ls | + 1 %a "[No Name]" line 1 | + {3:Press ENTER or type command to continue}^ | + ]]) + + -- when display doesn't contain msgsep, these options have no effect + feed_command("set display-=msgsep") + feed_command("ls") + screen:expect([[ + {1:~ }| + {1:~ }| + :ls | + 1 %a "[No Name]" line 1 | + {3:Press ENTER or type command to continue}^ | + ]]) + end) +end) + describe("'winhighlight' highlight", function() local screen @@ -683,6 +778,9 @@ describe("'winhighlight' highlight", function() [22] = {bold = true, foreground = Screen.colors.SeaGreen4}, [23] = {background = Screen.colors.LightMagenta}, [24] = {background = Screen.colors.WebGray}, + [25] = {bold = true, foreground = Screen.colors.Green1}, + [26] = {background = Screen.colors.Red}, + [27] = {background = Screen.colors.DarkBlue, bold = true, foreground = Screen.colors.Green1}, }) command("hi Background1 guibg=DarkBlue") command("hi Background2 guibg=DarkGreen") @@ -905,6 +1003,7 @@ describe("'winhighlight' highlight", function() end) it('background applies also to non-text', function() + command('set sidescroll=0') insert('Lorem ipsum dolor sit amet ') command('set shiftwidth=2') feed('>>') @@ -951,6 +1050,39 @@ describe("'winhighlight' highlight", function() ]]) end) + it("background doesn't override syntax background", function() + command('syntax on') + command('syntax keyword Foobar foobar') + command('syntax keyword Article the') + command('hi Foobar guibg=#FF0000') + command('hi Article guifg=#00FF00 gui=bold') + insert('the foobar was foobar') + screen:expect([[ + {25:the} {26:foobar} was {26:fooba}| + {26:^r} | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + + -- winhl=Normal:Group with background doesn't override syntax background, + -- but does combine with syntax foreground. + command('set winhl=Normal:Background1') + screen:expect([[ + {27:the}{1: }{26:foobar}{1: was }{26:fooba}| + {26:^r}{1: }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + | + ]]) + end) + it('can override NonText, Conceal and EndOfBuffer', function() curbufmeths.set_lines(0,-1,true, {"raa\000"}) command('call matchaddpos("Conceal", [[1,2]], 0, -1, {"conceal": "#"})') diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 53fd17c309..27e4066d9f 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -12,6 +12,7 @@ local insert = helpers.insert local meths = helpers.meths local neq = helpers.neq local ok = helpers.ok +local retry = helpers.retry local source = helpers.source local wait = helpers.wait local nvim = helpers.nvim @@ -62,6 +63,7 @@ local function common_setup(screen, inccommand, text) command("syntax on") command("set nohlsearch") command("hi Substitute guifg=red guibg=yellow") + command("set display-=msgsep") screen:attach() screen:set_default_attr_ids({ [1] = {foreground = Screen.colors.Fuchsia}, @@ -91,22 +93,30 @@ local function common_setup(screen, inccommand, text) end end -describe(":substitute, inccommand=split does not trigger preview", function() +describe(":substitute, inccommand=split", function() before_each(function() clear() common_setup(nil, "split", default_text) end) - it("if invoked by a script ", function() + -- Test the tests: verify that the `1==bufnr('$')` assertion + -- in the "no preview" tests (below) actually means something. + it("previews interactive cmdline", function() + feed(':%s/tw/MO/g') + retry(nil, 1000, function() + eq(2, eval("bufnr('$')")) + end) + end) + + it("no preview if invoked by a script", function() source('%s/tw/MO/g') wait() eq(1, eval("bufnr('$')")) - -- sanity check: assert the buffer state expect(default_text:gsub("tw", "MO")) end) - it("if invoked by feedkeys()", function() + it("no preview if invoked by feedkeys()", function() -- in a script... source([[:call feedkeys(":%s/tw/MO/g\<CR>")]]) wait() @@ -114,15 +124,12 @@ describe(":substitute, inccommand=split does not trigger preview", function() feed([[:call feedkeys(":%s/tw/MO/g\<CR>")<CR>]]) wait() eq(1, eval("bufnr('$')")) - -- sanity check: assert the buffer state expect(default_text:gsub("tw", "MO")) end) end) describe(":substitute, 'inccommand' preserves", function() - if helpers.pending_win32(pending) then return end - before_each(clear) it('listed buffers (:ls)', function() @@ -285,8 +292,6 @@ describe(":substitute, 'inccommand' preserves", function() end) describe(":substitute, 'inccommand' preserves undo", function() - if helpers.pending_win32(pending) then return end - local cases = { "", "split", "nosplit" } local substrings = { @@ -700,8 +705,6 @@ describe(":substitute, 'inccommand' preserves undo", function() end) describe(":substitute, inccommand=split", function() - if helpers.pending_win32(pending) then return end - local screen = Screen.new(30,15) before_each(function() @@ -1169,8 +1172,6 @@ describe(":substitute, inccommand=split", function() end) describe("inccommand=nosplit", function() - if helpers.pending_win32(pending) then return end - local screen = Screen.new(20,10) before_each(function() @@ -1356,8 +1357,6 @@ describe("inccommand=nosplit", function() end) describe(":substitute, 'inccommand' with a failing expression", function() - if helpers.pending_win32(pending) then return end - local screen = Screen.new(20,10) local cases = { "", "split", "nosplit" } @@ -1621,8 +1620,6 @@ describe("'inccommand' autocommands", function() end) describe("'inccommand' split windows", function() - if helpers.pending_win32(pending) then return end - local screen local function refresh() clear() @@ -1642,26 +1639,26 @@ describe("'inccommand' split windows", function() feed_command("split") feed(":%s/tw") screen:expect([[ - Inc substitution on {10:|}Inc substitution on| - {12:tw}o lines {10:|}{12:tw}o lines | - {10:|} | - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {11:[No Name] [+] }{10:|}{15:~ }| - Inc substitution on {10:|}{15:~ }| - {12:tw}o lines {10:|}{15:~ }| - {10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| + Inc substitution on {10:│}Inc substitution on| + {12:tw}o lines {10:│}{12:tw}o lines | + {10:│} | + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {11:[No Name] [+] }{10:│}{15:~ }| + Inc substitution on {10:│}{15:~ }| + {12:tw}o lines {10:│}{15:~ }| + {10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| {10:[No Name] [+] [No Name] [+] }| |2| {12:tw}o lines | {15:~ }| @@ -1681,20 +1678,20 @@ describe("'inccommand' split windows", function() feed(":%s/tw") screen:expect([[ - Inc substitution on {10:|}Inc substitution on| - {12:tw}o lines {10:|}{12:tw}o lines | - {10:|} | - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| - {15:~ }{10:|}{15:~ }| + Inc substitution on {10:│}Inc substitution on| + {12:tw}o lines {10:│}{12:tw}o lines | + {10:│} | + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| + {15:~ }{10:│}{15:~ }| {11:[No Name] [+] }{10:[No Name] [+] }| Inc substitution on | {12:tw}o lines | diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index 29d974b709..3dd9a2506e 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -1,11 +1,11 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed_command, nvim = helpers.clear, helpers.feed_command, helpers.nvim -local feed, next_message, eq = helpers.feed, helpers.next_message, helpers.eq +local feed, next_msg, eq = helpers.feed, helpers.next_msg, helpers.eq +local command = helpers.command local expect = helpers.expect +local write_file = helpers.write_file local Screen = require('test.functional.ui.screen') -if helpers.pending_win32(pending) then return end - describe('mappings', function() local cid @@ -17,7 +17,7 @@ describe('mappings', function() local check_mapping = function(mapping, expected) feed(mapping) - eq({'notification', 'mapped', {expected}}, next_message()) + eq({'notification', 'mapped', {expected}}, next_msg()) end before_each(function() @@ -126,3 +126,97 @@ describe('input utf sequences that contain CSI/K_SPECIAL', function() expect('…') end) end) + +describe('input non-printable chars', function() + after_each(function() + os.remove('Xtest-overwrite') + end) + + it("doesn't crash when echoing them back", function() + write_file("Xtest-overwrite", [[foobar]]) + clear() + local screen = Screen.new(60,8) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + [3] = {bold = true, foreground = Screen.colors.SeaGreen4} + }) + screen:attach() + command("set display-=msgsep") + + feed_command("e Xtest-overwrite") + screen:expect([[ + ^foobar | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + "Xtest-overwrite" [noeol] 1L, 6C | + ]]) + + -- The timestamp is in second resolution, wait two seconds to be sure. + screen:sleep(2000) + write_file("Xtest-overwrite", [[smurf]]) + feed_command("w") + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + "Xtest-overwrite" | + {2:WARNING: The file has been changed since reading it!!!} | + {3:Do you really want to write to it (y/n)?}^ | + ]]) + + feed("u") + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + "Xtest-overwrite" | + {2:WARNING: The file has been changed since reading it!!!} | + {3:Do you really want to write to it (y/n)?}u | + {3:Do you really want to write to it (y/n)?}^ | + ]]) + + feed("\005") + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + "Xtest-overwrite" | + {2:WARNING: The file has been changed since reading it!!!} | + {3:Do you really want to write to it (y/n)?}u | + {3:Do you really want to write to it (y/n)?} | + {3:Do you really want to write to it (y/n)?}^ | + ]]) + + feed("n") + screen:expect([[ + {1:~ }| + {1:~ }| + "Xtest-overwrite" | + {2:WARNING: The file has been changed since reading it!!!} | + {3:Do you really want to write to it (y/n)?}u | + {3:Do you really want to write to it (y/n)?} | + {3:Do you really want to write to it (y/n)?}n | + {3:Press ENTER or type command to continue}^ | + ]]) + + feed("<cr>") + screen:expect([[ + ^foobar | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + end) +end) diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 3daf92eea0..debd324977 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -3,8 +3,7 @@ local Screen = require('test.functional.ui.screen') local clear, feed, meths = helpers.clear, helpers.feed, helpers.meths local insert, feed_command = helpers.insert, helpers.feed_command local eq, funcs = helpers.eq, helpers.funcs - -if helpers.pending_win32(pending) then return end +local command = helpers.command describe('ui/mouse/input', function() local screen @@ -27,6 +26,7 @@ describe('ui/mouse/input', function() [4] = {reverse = true}, [5] = {bold = true, reverse = true}, }) + command("set display-=msgsep") feed('itesting<cr>mouse<cr>support and selection<esc>') screen:expect([[ testing | @@ -639,12 +639,12 @@ describe('ui/mouse/input', function() screen:try_resize(53, 14) feed_command('sp', 'vsp') screen:expect([[ - lines {4:|}lines | - to {4:|}to | - test {4:|}test | - mouse scrolling {4:|}mouse scrolling | - ^ {4:|} | - {0:~ }{4:|}{0:~ }| + lines {4:│}lines | + to {4:│}to | + test {4:│}test | + mouse scrolling {4:│}mouse scrolling | + ^ {4:│} | + {0:~ }{4:│}{0:~ }| {5:[No Name] [+] }{4:[No Name] [+] }| to | test | @@ -656,12 +656,12 @@ describe('ui/mouse/input', function() ]]) feed('<ScrollWheelDown><0,0>') screen:expect([[ - mouse scrolling {4:|}lines | - ^ {4:|}to | - {0:~ }{4:|}test | - {0:~ }{4:|}mouse scrolling | - {0:~ }{4:|} | - {0:~ }{4:|}{0:~ }| + mouse scrolling {4:│}lines | + ^ {4:│}to | + {0:~ }{4:│}test | + {0:~ }{4:│}mouse scrolling | + {0:~ }{4:│} | + {0:~ }{4:│}{0:~ }| {5:[No Name] [+] }{4:[No Name] [+] }| to | test | @@ -673,12 +673,12 @@ describe('ui/mouse/input', function() ]]) feed('<ScrollWheelUp><27,0>') screen:expect([[ - mouse scrolling {4:|}text | - ^ {4:|}with | - {0:~ }{4:|}many | - {0:~ }{4:|}lines | - {0:~ }{4:|}to | - {0:~ }{4:|}test | + mouse scrolling {4:│}text | + ^ {4:│}with | + {0:~ }{4:│}many | + {0:~ }{4:│}lines | + {0:~ }{4:│}to | + {0:~ }{4:│}test | {5:[No Name] [+] }{4:[No Name] [+] }| to | test | @@ -690,12 +690,12 @@ describe('ui/mouse/input', function() ]]) feed('<ScrollWheelUp><27,7><ScrollWheelUp>') screen:expect([[ - mouse scrolling {4:|}text | - ^ {4:|}with | - {0:~ }{4:|}many | - {0:~ }{4:|}lines | - {0:~ }{4:|}to | - {0:~ }{4:|}test | + mouse scrolling {4:│}text | + ^ {4:│}with | + {0:~ }{4:│}many | + {0:~ }{4:│}lines | + {0:~ }{4:│}to | + {0:~ }{4:│}test | {5:[No Name] [+] }{4:[No Name] [+] }| Inserting | text | @@ -708,6 +708,7 @@ describe('ui/mouse/input', function() end) it('horizontal scrolling', function() + command('set sidescroll=0') feed("<esc>:set nowrap<cr>") feed("a <esc>20Ab<esc>") @@ -752,17 +753,19 @@ describe('ui/mouse/input', function() feed_command('set concealcursor=n') feed_command('set nowrap') - feed_command('syntax match NonText "\\<amet\\>" conceal') - feed_command('syntax match NonText "\\cs\\|g." conceal cchar=X') - feed_command('syntax match NonText "\\%(lo\\|cl\\)." conceal') - feed_command('syntax match NonText "Lo" conceal cchar=Y') + feed_command('set shiftwidth=2 tabstop=4 list listchars=tab:>-') + feed_command('syntax match NonText "\\*" conceal') + feed_command('syntax match NonText "cats" conceal cchar=X') + feed_command('syntax match NonText "x" conceal cchar=>') + -- First column is there to retain the tabs. insert([[ - Lorem ipsum dolor sit amet, consetetur sadipscing elitr. - Stet clita kasd gubergren, no sea takimata sanctus est. + |Section *t1* + | *t2* *t3* *t4* + |x 私は猫が大好き *cats* ✨🐈✨ ]]) - feed('gg') + feed('gg<c-v>Gxgg') end) it('(level 1) click on non-wrapped lines', function() @@ -770,93 +773,138 @@ describe('ui/mouse/input', function() feed('<esc><LeftMouse><0,0>') screen:expect([[ - {c:^Y}rem ip{c:X}um do{c: } {c:X}it {c: }, con| - {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en, no| + ^Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}| | {0:~ }| {0:~ }| - {0:~ }| | ]]) feed('<esc><LeftMouse><1,0>') screen:expect([[ - {c:Y}^rem ip{c:X}um do{c: } {c:X}it {c: }, con| - {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en, no| + S^ection{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}| | {0:~ }| {0:~ }| + | + ]]) + + feed('<esc><LeftMouse><21,0>') + screen:expect([[ + Section{0:>>--->--->---}{c: }^t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}| + | + {0:~ }| {0:~ }| | ]]) - feed('<esc><LeftMouse><15,0>') + feed('<esc><LeftMouse><21,1>') screen:expect([[ - {c:Y}rem ip{c:X}um do{c: } {c:^X}it {c: }, con| - {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en, no| + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t^3{c: } {c: }| + {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}| | {0:~ }| {0:~ }| + | + ]]) + + feed('<esc><LeftMouse><0,2>') + screen:expect([[ + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + {c:^>} 私は猫が大好き{0:>---}{c: X } {0:>}| + | + {0:~ }| {0:~ }| | ]]) - feed('<esc><LeftMouse><15,1>') + feed('<esc><LeftMouse><7,2>') screen:expect([[ - {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: }, con| - {c:X}tet {c: }ta ka{c:X}d {c:X}^ber{c:X}en, no| + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + {c:>} 私は^猫が大好き{0:>---}{c: X } {0:>}| | {0:~ }| {0:~ }| + | + ]]) + + feed('<esc><LeftMouse><21,2>') + screen:expect([[ + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + {c:>} 私は猫が大好き{0:>---}{c: ^X } {0:>}| + | + {0:~ }| {0:~ }| | ]]) + end) -- level 1 - non wrapped it('(level 1) click on wrapped lines', function() feed_command('let &conceallevel=1', 'let &wrap=1', 'echo') - feed('<esc><LeftMouse><0,0>') + feed('<esc><LeftMouse><24,1>') screen:expect([[ - {c:^Y}rem ip{c:X}um do{c: } {c:X}it {c: } | - , con{c:X}etetur {c:X}adip{c:X}cin{c:X} | - elitr. | - {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c:^ }| + t4{c: } | + {c:>} 私は猫が大好き{0:>---}{c: X} | + {c: } ✨🐈✨ | + | | ]]) - feed('<esc><LeftMouse><6,1>') + feed('<esc><LeftMouse><0,2>') screen:expect([[ - {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: } | - , con{c:X}^etetur {c:X}adip{c:X}cin{c:X} | - elitr. | - {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + ^t4{c: } | + {c:>} 私は猫が大好き{0:>---}{c: X} | + {c: } ✨🐈✨ | + | | ]]) - feed('<esc><LeftMouse><15,1>') + feed('<esc><LeftMouse><8,3>') screen:expect([[ - {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: } | - , con{c:X}etetur {c:X}a^dip{c:X}cin{c:X} | - elitr. | - {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + t4{c: } | + {c:>} 私は猫^が大好き{0:>---}{c: X} | + {c: } ✨🐈✨ | + | | ]]) - feed('<esc><LeftMouse><15,3>') + feed('<esc><LeftMouse><21,3>') screen:expect([[ - {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: } | - , con{c:X}etetur {c:X}adip{c:X}cin{c:X} | - elitr. | - {c:X}tet {c: }ta ka{c:X}d {c:X}^ber{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + t4{c: } | + {c:>} 私は猫が大好き{0:>---}{c: ^X} | + {c: } ✨🐈✨ | + | + | + ]]) + + feed('<esc><LeftMouse><4,4>') + screen:expect([[ + Section{0:>>--->--->---}{c: }t1{c: } | + {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| + t4{c: } | + {c:>} 私は猫が大好き{0:>---}{c: X} | + {c: } ✨^🐈✨ | + | | ]]) end) -- level 1 - wrapped @@ -865,46 +913,68 @@ describe('ui/mouse/input', function() it('(level 2) click on non-wrapped lines', function() feed_command('let &conceallevel=2', 'echo') - feed('<esc><LeftMouse><0,0>') + feed('<esc><LeftMouse><20,0>') screen:expect([[ - {c:^Y}rem ip{c:X}um do {c:X}it , con{c:X}e| - {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en, no | + Section{0:>>--->--->---}^t1 | + {0:>--->--->---} t2 t3 t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }| {0:~ }| - {0:~ }| | ]]) - feed('<esc><LeftMouse><1,0>') + feed('<esc><LeftMouse><14,1>') screen:expect([[ - {c:Y}^rem ip{c:X}um do {c:X}it , con{c:X}e| - {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en, no | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} ^t2 t3 t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }| {0:~ }| - {0:~ }| | ]]) - feed('<esc><LeftMouse><15,0>') + feed('<esc><LeftMouse><18,1>') screen:expect([[ - {c:Y}rem ip{c:X}um do {c:X}^it , con{c:X}e| - {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en, no | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t^3 t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }| {0:~ }| + | + ]]) + + feed('<esc><LeftMouse><0,2>') -- Weirdness + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 t4 | + {c:^>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| + | + {0:~ }| {0:~ }| | ]]) - feed('<esc><LeftMouse><15,1>') + feed('<esc><LeftMouse><8,2>') screen:expect([[ - {c:Y}rem ip{c:X}um do {c:X}it , con{c:X}e| - {c:X}tet ta ka{c:X}d {c:X}b^er{c:X}en, no | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 t4 | + {c:>} 私は猫^が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }| {0:~ }| + | + ]]) + + feed('<esc><LeftMouse><20,2>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 t4 | + {c:>} 私は猫が大好き{0:>---}{c:^X} ✨{0:>}| + | + {0:~ }| {0:~ }| | ]]) @@ -913,47 +983,108 @@ describe('ui/mouse/input', function() it('(level 2) click on wrapped lines', function() feed_command('let &conceallevel=2', 'let &wrap=1', 'echo') - feed('<esc><LeftMouse><0,0>') + feed('<esc><LeftMouse><20,0>') screen:expect([[ - {c:^Y}rem ip{c:X}um do {c:X}it | - , con{c:X}etetur {c:X}adip{c:X}cin{c:X} | - elitr. | - {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}^t1 | + {0:>--->--->---} t2 t3 | + t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} | + ✨🐈✨ | + | | ]]) - feed('<esc><LeftMouse><6,1>') + feed('<esc><LeftMouse><14,1>') screen:expect([[ - {c:Y}rem ip{c:X}um do {c:X}it | - , con{c:X}^etetur {c:X}adip{c:X}cin{c:X} | - elitr. | - {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} ^t2 t3 | + t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} | + ✨🐈✨ | + | | ]]) - feed('<esc><LeftMouse><15,1>') + feed('<esc><LeftMouse><18,1>') screen:expect([[ - {c:Y}rem ip{c:X}um do {c:X}it | - , con{c:X}etetur {c:X}a^dip{c:X}cin{c:X} | - elitr. | - {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t^3 | + t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} | + ✨🐈✨ | + | | ]]) - feed('<esc><LeftMouse><15,3>') + -- NOTE: The click would ideally be on the 't' in 't4', but wrapping + -- caused the invisible '*' right before 't4' to remain on the previous + -- screen line. This is being treated as expected because fixing this is + -- out of scope for mouse clicks. Should the wrapping behavior of + -- concealed characters change in the future, this case should be + -- reevaluated. + feed('<esc><LeftMouse><0,2>') screen:expect([[ - {c:Y}rem ip{c:X}um do {c:X}it | - , con{c:X}etetur {c:X}adip{c:X}cin{c:X} | - elitr. | - {c:X}tet ta ka{c:X}d {c:X}b^er{c:X}en | - , no {c:X}ea takimata {c:X}anctu{c:X}| - e{c:X}t. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 ^ | + t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} | + ✨🐈✨ | + | + | + ]]) + + feed('<esc><LeftMouse><1,2>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t^4 | + {c:>} 私は猫が大好き{0:>---}{c:X} | + ✨🐈✨ | + | + | + ]]) + + feed('<esc><LeftMouse><0,3>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + {c:^>} 私は猫が大好き{0:>---}{c:X} | + ✨🐈✨ | + | + | + ]]) + + feed('<esc><LeftMouse><20,3>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + {c:>} 私は猫が大好き{0:>---}{c:^X} | + ✨🐈✨ | + | + | + ]]) + + feed('<esc><LeftMouse><1,4>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} | + ^✨🐈✨ | + | + | + ]]) + + feed('<esc><LeftMouse><5,4>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + {c:>} 私は猫が大好き{0:>---}{c:X} | + ✨🐈^✨ | + | | ]]) end) -- level 2 - wrapped @@ -962,47 +1093,47 @@ describe('ui/mouse/input', function() it('(level 3) click on non-wrapped lines', function() feed_command('let &conceallevel=3', 'echo') - feed('<esc><LeftMouse><0,0>') + feed('<esc><LeftMouse><0,2>') screen:expect([[ - ^rem ipum do it , conetetu| - tet ta kad beren, no ea t| + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 t4 | + ^ 私は猫が大好き{0:>----} ✨🐈| | {0:~ }| {0:~ }| - {0:~ }| | ]]) - feed('<esc><LeftMouse><1,0>') + feed('<esc><LeftMouse><1,2>') screen:expect([[ - r^em ipum do it , conetetu| - tet ta kad beren, no ea t| + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 t4 | + ^私は猫が大好き{0:>----} ✨🐈| | {0:~ }| {0:~ }| - {0:~ }| | ]]) - feed('<esc><LeftMouse><15,0>') + feed('<esc><LeftMouse><13,2>') screen:expect([[ - rem ipum do it ^, conetetu| - tet ta kad beren, no ea t| + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 t4 | + 私は猫が大好^き{0:>----} ✨🐈| | {0:~ }| {0:~ }| - {0:~ }| | ]]) - feed('<esc><LeftMouse><15,1>') + feed('<esc><LeftMouse><20,2>') screen:expect([[ - rem ipum do it , conetetu| - tet ta kad bere^n, no ea t| + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 t4 | + 私は猫が大好き{0:>----}^ ✨🐈| | {0:~ }| {0:~ }| - {0:~ }| | ]]) end) -- level 3 - non wrapped @@ -1010,49 +1141,94 @@ describe('ui/mouse/input', function() it('(level 3) click on wrapped lines', function() feed_command('let &conceallevel=3', 'let &wrap=1', 'echo') - feed('<esc><LeftMouse><0,0>') + feed('<esc><LeftMouse><14,1>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} ^t2 t3 | + t4 | + 私は猫が大好き{0:>----} | + ✨🐈✨ | + | + | + ]]) + + feed('<esc><LeftMouse><18,1>') screen:expect([[ - ^rem ipum do it | - , conetetur adipcin | - elitr. | - tet ta kad beren | - , no ea takimata anctu | - et. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t^3 | + t4 | + 私は猫が大好き{0:>----} | + ✨🐈✨ | + | | ]]) - feed('<esc><LeftMouse><6,1>') + feed('<esc><LeftMouse><1,2>') screen:expect([[ - rem ipum do it | - , cone^tetur adipcin | - elitr. | - tet ta kad beren | - , no ea takimata anctu | - et. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t^4 | + 私は猫が大好き{0:>----} | + ✨🐈✨ | + | | ]]) - feed('<esc><LeftMouse><15,1>') + feed('<esc><LeftMouse><0,3>') screen:expect([[ - rem ipum do it | - , conetetur adi^pcin | - elitr. | - tet ta kad beren | - , no ea takimata anctu | - et. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + ^ 私は猫が大好き{0:>----} | + ✨🐈✨ | + | | ]]) - feed('<esc><LeftMouse><15,3>') + feed('<esc><LeftMouse><20,3>') screen:expect([[ - rem ipum do it | - , conetetur adipcin | - elitr. | - tet ta kad bere^n | - , no ea takimata anctu | - et. | + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + 私は猫が大好き{0:>----}^ | + ✨🐈✨ | + | | ]]) + + feed('<esc><LeftMouse><1,4>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + 私は猫が大好き{0:>----} | + ^✨🐈✨ | + | + | + ]]) + + feed('<esc><LeftMouse><3,4>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + 私は猫が大好き{0:>----} | + ✨^🐈✨ | + | + | + ]]) + + feed('<esc><LeftMouse><5,4>') + screen:expect([[ + Section{0:>>--->--->---}t1 | + {0:>--->--->---} t2 t3 | + t4 | + 私は猫が大好き{0:>----} | + ✨🐈^✨ | + | + | + ]]) + end) -- level 3 - wrapped end) end) diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua new file mode 100644 index 0000000000..62b08c0967 --- /dev/null +++ b/test/functional/ui/options_spec.lua @@ -0,0 +1,110 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq + +describe('ui receives option updates', function() + local screen + + before_each(function() + clear() + screen = Screen.new(20,5) + end) + + after_each(function() + screen:detach() + end) + + local defaults = { + ambiwidth='single', + arabicshape=true, + emoji=true, + guifont='', + guifontset='', + guifontwide='', + linespace=0, + showtabline=1, + termguicolors=false, + ext_cmdline=false, + ext_popupmenu=false, + ext_tabline=false, + ext_wildmenu=false, + } + + it("for defaults", function() + screen:attach() + screen:expect(function() + eq(defaults, screen.options) + end) + end) + + it("when setting options", function() + screen:attach() + local changed = {} + for k,v in pairs(defaults) do + changed[k] = v + end + + command("set termguicolors") + changed.termguicolors = true + screen:expect(function() + eq(changed, screen.options) + end) + + command("set guifont=Comic\\ Sans") + changed.guifont = "Comic Sans" + screen:expect(function() + eq(changed, screen.options) + end) + + command("set showtabline=0") + changed.showtabline = 0 + screen:expect(function() + eq(changed, screen.options) + end) + + command("set linespace=13") + changed.linespace = 13 + screen:expect(function() + eq(changed, screen.options) + end) + + command("set linespace=-11") + changed.linespace = -11 + screen:expect(function() + eq(changed, screen.options) + end) + + command("set all&") + screen:expect(function() + eq(defaults, screen.options) + end) + end) + + it('with UI extensions', function() + local changed = {} + for k,v in pairs(defaults) do + changed[k] = v + end + + screen:attach({ext_cmdline=true, ext_wildmenu=true}) + changed.ext_cmdline = true + changed.ext_wildmenu = true + screen:expect(function() + eq(changed, screen.options) + end) + + screen:set_option('ext_popupmenu', true) + changed.ext_popupmenu = true + screen:expect(function() + eq(changed, screen.options) + end) + + screen:set_option('ext_wildmenu', false) + changed.ext_wildmenu = false + screen:expect(function() + eq(changed, screen.options) + end) + end) +end) diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index c6d564e8dc..02ca566bd8 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -1,14 +1,24 @@ -local session = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local helpers = require('test.functional.helpers')(after_each) local child_session = require('test.functional.terminal.helpers') - -if session.pending_win32(pending) then return end +local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir +local eq = helpers.eq +local eval = helpers.eval +local feed = helpers.feed +local feed_command = helpers.feed_command +local iswin = helpers.iswin +local clear = helpers.clear +local command = helpers.command +local nvim_dir = helpers.nvim_dir describe("shell command :!", function() + if helpers.pending_win32(pending) then return end + local screen before_each(function() - session.clear() - screen = child_session.screen_setup(0, '["'..session.nvim_prog.. - '", "-u", "NONE", "-i", "NONE", "--cmd", "'..session.nvim_set..'"]') + clear() + screen = child_session.screen_setup(0, '["'..helpers.nvim_prog.. + '", "-u", "NONE", "-i", "NONE", "--cmd", "'..helpers.nvim_set..'"]') screen:expect([[ {1: } | {4:~ }| @@ -30,18 +40,18 @@ describe("shell command :!", function() -- to avoid triggering a UI flush. child_session.feed_data(":!printf foo; sleep 200\n") screen:expect([[ + | {4:~ }| {4:~ }| - {4:~ }| + {5: }| :!printf foo; sleep 200 | - | foo | {3:-- TERMINAL --} | ]]) end) it("throttles shell-command output greater than ~10KB", function() - if os.getenv("TRAVIS") and session.os_name() == "osx" then + if os.getenv("TRAVIS") and helpers.os_name() == "osx" then pending("[Unreliable on Travis macOS.]", function() end) return end @@ -56,13 +66,166 @@ describe("shell command :!", function() -- Final chunk of output should always be displayed, never skipped. -- (Throttling is non-deterministic, this test is merely a sanity check.) screen:expect([[ - XXXXXXXXXX 2996 | XXXXXXXXXX 2997 | XXXXXXXXXX 2998 | XXXXXXXXXX 2999 | XXXXXXXXXX 3000 | + | {10:Press ENTER or type command to continue}{1: } | {3:-- TERMINAL --} | ]]) end) end) + +describe("shell command :!", function() + before_each(function() + clear() + end) + + it("cat a binary file #4142", function() + feed(":exe 'silent !cat '.shellescape(v:progpath)<CR>") + eq(2, eval('1+1')) -- Still alive? + end) + + it([[display \x08 char #4142]], function() + feed(":silent !echo \08<CR>") + eq(2, eval('1+1')) -- Still alive? + end) + + it([[handles control codes]], function() + if iswin() then + pending('missing printf', function() end) + return + end + local screen = Screen.new(50, 4) + screen:attach() + command("set display-=msgsep") + -- Print TAB chars. #2958 + feed([[:!printf '1\t2\t3'<CR>]]) + screen:expect([[ + ~ | + :!printf '1\t2\t3' | + 1 2 3 | + Press ENTER or type command to continue^ | + ]]) + feed([[<CR>]]) + -- Print BELL control code. #4338 + screen.bell = false + feed([[:!printf '\x07\x07\x07\x07text'<CR>]]) + screen:expect([[ + ~ | + :!printf '\x07\x07\x07\x07text' | + text | + Press ENTER or type command to continue^ | + ]], nil, nil, function() + eq(true, screen.bell) + end) + feed([[<CR>]]) + -- Print BS control code. + feed([[:echo system('printf ''\x08\n''')<CR>]]) + screen:expect([[ + ~ | + ^H | + | + Press ENTER or type command to continue^ | + ]]) + feed([[<CR>]]) + -- Print LF control code. + feed([[:!printf '\n'<CR>]]) + screen:expect([[ + :!printf '\n' | + | + | + Press ENTER or type command to continue^ | + ]]) + feed([[<CR>]]) + end) + + describe('', function() + local screen + before_each(function() + rmdir('bang_filter_spec') + mkdir('bang_filter_spec') + write_file('bang_filter_spec/f1', 'f1') + write_file('bang_filter_spec/f2', 'f2') + write_file('bang_filter_spec/f3', 'f3') + screen = Screen.new(53,10) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {foreground = Screen.colors.Blue1}, + [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [4] = {bold = true, reverse = true}, + }) + screen:attach() + end) + + after_each(function() + rmdir('bang_filter_spec') + end) + + it("doesn't truncate Last line of shell output #3269", function() + command(helpers.iswin() + and [[nnoremap <silent>\l :!dir /b bang_filter_spec<cr>]] + or [[nnoremap <silent>\l :!ls bang_filter_spec<cr>]]) + local result = (helpers.iswin() + and [[:!dir /b bang_filter_spec]] + or [[:!ls bang_filter_spec ]]) + feed([[\l]]) + screen:expect([[ + | + {1:~ }| + {1:~ }| + {4: }| + ]]..result..[[ | + f1 | + f2 | + f3 | + | + {3:Press ENTER or type command to continue}^ | + ]]) + end) + + it('handles binary and multibyte data', function() + feed_command('!cat test/functional/fixtures/shell_data.txt') + screen.bell = false + screen:expect([[ + | + {1:~ }| + {4: }| + :!cat test/functional/fixtures/shell_data.txt | + {2:^@^A^B^C^D^E^F^H} | + {2:^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_} | + ö 한글 {2:<a5><c3>} | + t {2:<ff>} | + | + {3:Press ENTER or type command to continue}^ | + ]], nil, nil, function() + eq(true, screen.bell) + end) + end) + + it('handles multibyte sequences split over buffer boundaries', function() + command('cd '..nvim_dir) + local cmd + if iswin() then + cmd = '!shell-test UTF-8 ' + else + cmd = '!./shell-test UTF-8' + end + feed_command(cmd) + -- Note: only the first example of split composed char works + screen:expect([[ + | + {4: }| + :]]..cmd..[[ | + å | + ref: å̲ | + 1: å̲ | + 2: å ̲ | + 3: å ̲ | + | + {3:Press ENTER or type command to continue}^ | + ]]) + end) + end) +end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index a6b7fb2997..52e108f389 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -137,6 +137,7 @@ function Screen.new(width, height) visual_bell = false, suspended = false, mode = 'normal', + options = {}, _default_attr_ids = nil, _default_attr_ignore = nil, _mouse_enabled = true, @@ -176,6 +177,10 @@ function Screen:try_resize(columns, rows) self:sleep(0.1) end +function Screen:set_option(option, value) + uimeths.set_option(option, value) +end + -- Asserts that `expected` eventually matches the screen state. -- -- expected: Expected screen state (string). Each line represents a screen @@ -450,6 +455,9 @@ function Screen:_handle_visual_bell() self.visual_bell = true end +function Screen:_handle_default_colors_set() +end + function Screen:_handle_update_fg(fg) self._fg = fg end @@ -478,6 +486,10 @@ function Screen:_handle_set_icon(icon) self.icon = icon end +function Screen:_handle_option_set(name, value) + self.options[name] = value +end + function Screen:_clear_block(top, bot, left, right) for i = top, bot do self:_clear_row_section(i, left, right) diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index b31d9cb32f..8a1f9b0d19 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -4,7 +4,6 @@ local spawn, set_session, clear = helpers.spawn, helpers.set_session, helpers.cl local feed, command = helpers.feed, helpers.command local insert = helpers.insert local eq = helpers.eq -local eval = helpers.eval local iswin = helpers.iswin describe('screen', function() @@ -189,12 +188,12 @@ describe('Screen', function() command('vsp') command('vsp') screen:expect([[ - ^ {3:|} {3:|} | - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| + ^ {3:│} {3:│} | + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| {1:[No Name] }{3:[No Name] [No Name] }| | {0:~ }| @@ -206,12 +205,12 @@ describe('Screen', function() ]]) insert('hello') screen:expect([[ - hell^o {3:|}hello {3:|}hello | - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| + hell^o {3:│}hello {3:│}hello | + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }| hello | {0:~ }| @@ -232,12 +231,12 @@ describe('Screen', function() command('vsp') insert('hello') screen:expect([[ - hell^o {3:|}hello {3:|}hello | - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| + hell^o {3:│}hello {3:│}hello | + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }| hello | {0:~ }| @@ -269,12 +268,12 @@ describe('Screen', function() command('tabprevious') screen:expect([[ {2: }{6:4}{2:+ [No Name] }{4: + [No Name] }{3: }{4:X}| - hell^o {3:|}hello {3:|}hello | - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| - {0:~ }{3:|}{0:~ }{3:|}{0:~ }| + hell^o {3:│}hello {3:│}hello | + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| + {0:~ }{3:│}{0:~ }{3:│}{0:~ }| {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }| hello | {0:~ }| @@ -355,7 +354,8 @@ describe('Screen', function() ]]) end) - it('execute command with multi-line output', function() + it('execute command with multi-line output without msgsep', function() + command("set display-=msgsep") feed(':ls<cr>') screen:expect([[ {0:~ }| @@ -375,6 +375,28 @@ describe('Screen', function() ]]) feed('<cr>') -- skip the "Press ENTER..." state or tests will hang end) + + it('execute command with multi-line output and with msgsep', function() + command("set display+=msgsep") + feed(':ls<cr>') + screen:expect([[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {1: }| + :ls | + 1 %a "[No Name]" line 1 | + {7:Press ENTER or type command to continue}^ | + ]]) + feed('<cr>') -- skip the "Press ENTER..." state or tests will hang + end) end) describe('scrolling and clearing', function() @@ -398,12 +420,12 @@ describe('Screen', function() command('vsp') command('vsp') screen:expect([[ - and {3:|}and {3:|}and | - clearing {3:|}clearing {3:|}clearing | - in {3:|}in {3:|}in | - split {3:|}split {3:|}split | - windows {3:|}windows {3:|}windows | - ^ {3:|} {3:|} | + and {3:│}and {3:│}and | + clearing {3:│}clearing {3:│}clearing | + in {3:│}in {3:│}in | + split {3:│}split {3:│}split | + windows {3:│}windows {3:│}windows | + ^ {3:│} {3:│} | {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }| clearing | in | @@ -418,12 +440,12 @@ describe('Screen', function() it('only affects the current scroll region', function() feed('6k') screen:expect([[ - ^scrolling {3:|}and {3:|}and | - and {3:|}clearing {3:|}clearing | - clearing {3:|}in {3:|}in | - in {3:|}split {3:|}split | - split {3:|}windows {3:|}windows | - windows {3:|} {3:|} | + ^scrolling {3:│}and {3:│}and | + and {3:│}clearing {3:│}clearing | + clearing {3:│}in {3:│}in | + in {3:│}split {3:│}split | + split {3:│}windows {3:│}windows | + windows {3:│} {3:│} | {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }| clearing | in | @@ -435,12 +457,12 @@ describe('Screen', function() ]]) feed('<c-w>l') screen:expect([[ - scrolling {3:|}and {3:|}and | - and {3:|}clearing {3:|}clearing | - clearing {3:|}in {3:|}in | - in {3:|}split {3:|}split | - split {3:|}windows {3:|}windows | - windows {3:|}^ {3:|} | + scrolling {3:│}and {3:│}and | + and {3:│}clearing {3:│}clearing | + clearing {3:│}in {3:│}in | + in {3:│}split {3:│}split | + split {3:│}windows {3:│}windows | + windows {3:│}^ {3:│} | {3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }| clearing | in | @@ -452,12 +474,12 @@ describe('Screen', function() ]]) feed('gg') screen:expect([[ - scrolling {3:|}^Inserting {3:|}and | - and {3:|}text {3:|}clearing | - clearing {3:|}with {3:|}in | - in {3:|}many {3:|}split | - split {3:|}lines {3:|}windows | - windows {3:|}to {3:|} | + scrolling {3:│}^Inserting {3:│}and | + and {3:│}text {3:│}clearing | + clearing {3:│}with {3:│}in | + in {3:│}many {3:│}split | + split {3:│}lines {3:│}windows | + windows {3:│}to {3:│} | {3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }| clearing | in | @@ -469,12 +491,12 @@ describe('Screen', function() ]]) feed('7j') screen:expect([[ - scrolling {3:|}with {3:|}and | - and {3:|}many {3:|}clearing | - clearing {3:|}lines {3:|}in | - in {3:|}to {3:|}split | - split {3:|}test {3:|}windows | - windows {3:|}^scrolling {3:|} | + scrolling {3:│}with {3:│}and | + and {3:│}many {3:│}clearing | + clearing {3:│}lines {3:│}in | + in {3:│}to {3:│}split | + split {3:│}test {3:│}windows | + windows {3:│}^scrolling {3:│} | {3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }| clearing | in | @@ -486,12 +508,12 @@ describe('Screen', function() ]]) feed('2j') screen:expect([[ - scrolling {3:|}lines {3:|}and | - and {3:|}to {3:|}clearing | - clearing {3:|}test {3:|}in | - in {3:|}scrolling {3:|}split | - split {3:|}and {3:|}windows | - windows {3:|}^clearing {3:|} | + scrolling {3:│}lines {3:│}and | + and {3:│}to {3:│}clearing | + clearing {3:│}test {3:│}in | + in {3:│}scrolling {3:│}split | + split {3:│}and {3:│}windows | + windows {3:│}^clearing {3:│} | {3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }| clearing | in | @@ -503,12 +525,12 @@ describe('Screen', function() ]]) feed('5k') screen:expect([[ - scrolling {3:|}^lines {3:|}and | - and {3:|}to {3:|}clearing | - clearing {3:|}test {3:|}in | - in {3:|}scrolling {3:|}split | - split {3:|}and {3:|}windows | - windows {3:|}clearing {3:|} | + scrolling {3:│}^lines {3:│}and | + and {3:│}to {3:│}clearing | + clearing {3:│}test {3:│}in | + in {3:│}scrolling {3:│}split | + split {3:│}and {3:│}windows | + windows {3:│}clearing {3:│} | {3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }| clearing | in | @@ -520,12 +542,12 @@ describe('Screen', function() ]]) feed('k') screen:expect([[ - scrolling {3:|}^many {3:|}and | - and {3:|}lines {3:|}clearing | - clearing {3:|}to {3:|}in | - in {3:|}test {3:|}split | - split {3:|}scrolling {3:|}windows | - windows {3:|}and {3:|} | + scrolling {3:│}^many {3:│}and | + and {3:│}lines {3:│}clearing | + clearing {3:│}to {3:│}in | + in {3:│}test {3:│}split | + split {3:│}scrolling {3:│}windows | + windows {3:│}and {3:│} | {3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }| clearing | in | @@ -573,6 +595,7 @@ describe('Screen', function() command('nnoremap <F1> :echo "TEST"<CR>') feed(':ls<CR>') screen:expect([[ + | {0:~ }| {0:~ }| {0:~ }| @@ -582,8 +605,7 @@ describe('Screen', function() {0:~ }| {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| + {1: }| :ls | 1 %a "[No Name]" line 1 | {7:Press ENTER or type command to continue}^ | @@ -608,21 +630,3 @@ describe('Screen', function() end) end) end) - -describe('nvim_ui_attach()', function() - before_each(function() - clear() - end) - it('handles very large width/height #2180', function() - local screen = Screen.new(999, 999) - screen:attach() - eq(999, eval('&lines')) - eq(999, eval('&columns')) - end) - it('invalid option returns error', function() - local screen = Screen.new() - local status, rv = pcall(function() screen:attach({foo={'foo'}}) end) - eq(false, status) - eq('No such ui option', rv:match("No such .*")) - end) -end) diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index 11b18d015f..9f273e8dc9 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -2,8 +2,8 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local feed_command = helpers.feed_command - -if helpers.pending_win32(pending) then return end +local eq = helpers.eq +local eval = helpers.eval describe('search highlighting', function() local screen @@ -101,7 +101,30 @@ describe('search highlighting', function() feed("gg/li") screen:expect([[ the first {3:li}ne | - in a little file | + in a {2:li}ttle file | + | + {1:~ }| + {1:~ }| + {1:~ }| + /li^ | + ]]) + + -- check that consecutive matches are caught by C-g/C-t + feed("<C-g>") + screen:expect([[ + the first {2:li}ne | + in a {3:li}ttle file | + | + {1:~ }| + {1:~ }| + {1:~ }| + /li^ | + ]]) + + feed("<C-t>") + screen:expect([[ + the first {3:li}ne | + in a {2:li}ttle file | | {1:~ }| {1:~ }| @@ -134,7 +157,7 @@ describe('search highlighting', function() feed("/fir") screen:expect([[ the {3:fir}st line | - in a {2:lit}tle file | + in a little file | | {1:~ }| {1:~ }| @@ -146,13 +169,107 @@ describe('search highlighting', function() feed("<esc>/ttle") screen:expect([[ the first line | - in a {2:li}{3:ttle} file | + in a li{3:ttle} file | | {1:~ }| {1:~ }| {1:~ }| /ttle^ | ]]) + + -- cancelling search resets to the old search term + feed('<esc>') + screen:expect([[ + the first line | + in a {2:^lit}tle file | + | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + eq('lit', eval('@/')) + + -- cancelling inc search restores the hl state + feed(':noh<cr>') + screen:expect([[ + the first line | + in a ^little file | + | + {1:~ }| + {1:~ }| + {1:~ }| + :noh | + ]]) + + feed('/first') + screen:expect([[ + the {3:first} line | + in a little file | + | + {1:~ }| + {1:~ }| + {1:~ }| + /first^ | + ]]) + feed('<esc>') + screen:expect([[ + the first line | + in a ^little file | + | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + + -- test that pressing C-g in an empty command line does not move the cursor + feed('/<C-g>') + screen:expect([[ + the first line | + in a little file | + | + {1:~ }| + {1:~ }| + {1:~ }| + /^ | + ]]) + + -- same, for C-t + feed('<ESC>/<C-t>') + screen:expect([[ + the first line | + in a little file | + | + {1:~ }| + {1:~ }| + {1:~ }| + /^ | + ]]) + + -- 8.0.1304, test that C-g and C-t works with incsearch and empty pattern + feed('<esc>/fi<CR>') + feed('//') + screen:expect([[ + the {3:fi}rst line | + in a little {2:fi}le | + | + {1:~ }| + {1:~ }| + {1:~ }| + //^ | + ]]) + + feed('<C-g>') + screen:expect([[ + the {2:fi}rst line | + in a little {3:fi}le | + | + {1:~ }| + {1:~ }| + {1:~ }| + //^ | + ]]) end) it('works with incsearch and offset', function() @@ -165,7 +282,7 @@ describe('search highlighting', function() feed("gg/mat/e") screen:expect([[ not the {3:mat}ch you're looking for | - the match is here | + the {2:mat}ch is here | {1:~ }| {1:~ }| {1:~ }| @@ -176,7 +293,7 @@ describe('search highlighting', function() -- Search with count and /e offset fixed in Vim patch 7.4.532. feed("<esc>2/mat/e") screen:expect([[ - not the match you're looking for | + not the {2:mat}ch you're looking for | the {3:mat}ch is here | {1:~ }| {1:~ }| diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua index e5c96f2ec0..c00d99cf90 100644 --- a/test/functional/ui/sign_spec.lua +++ b/test/functional/ui/sign_spec.lua @@ -2,8 +2,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed, command = helpers.clear, helpers.feed, helpers.command -if helpers.pending_win32(pending) then return end - describe('Signs', function() local screen diff --git a/test/functional/ui/spell_spec.lua b/test/functional/ui/spell_spec.lua new file mode 100644 index 0000000000..913f1b9bed --- /dev/null +++ b/test/functional/ui/spell_spec.lua @@ -0,0 +1,49 @@ +-- Test for scenarios involving 'spell' + +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local feed = helpers.feed +local feed_command = helpers.feed_command +local insert = helpers.insert + +describe("'spell'", function() + local screen + + before_each(function() + clear() + screen = Screen.new(80, 8) + screen:attach() + screen:set_default_attr_ids( { + [0] = {bold=true, foreground=Screen.colors.Blue}, + [1] = {special = Screen.colors.Red, undercurl = true} + }) + end) + + after_each(function() + screen:detach() + end) + + it('joins long lines #7937', function() + feed_command('set spell') + insert([[ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, + quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat + non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + ]]) + feed('ggJJJJJJ0') + screen:expect([[ + {1:^Lorem} {1:ipsum} dolor sit {1:amet}, {1:consectetur} {1:adipiscing} {1:elit}, {1:sed} do {1:eiusmod} {1:tempor} {1:i}| + {1:ncididunt} {1:ut} {1:labore} {1:et} {1:dolore} {1:magna} {1:aliqua}. {1:Ut} {1:enim} ad minim {1:veniam}, {1:quis} {1:nostru}| + {1:d} {1:exercitation} {1:ullamco} {1:laboris} {1:nisi} {1:ut} {1:aliquip} ex ea {1:commodo} {1:consequat}. {1:Duis} {1:aut}| + {1:e} {1:irure} dolor in {1:reprehenderit} in {1:voluptate} {1:velit} {1:esse} {1:cillum} {1:dolore} {1:eu} {1:fugiat} {1:n}| + {1:ulla} {1:pariatur}. {1:Excepteur} {1:sint} {1:occaecat} {1:cupidatat} non {1:proident}, {1:sunt} in culpa {1:qui}| + {1:officia} {1:deserunt} {1:mollit} {1:anim} id est {1:laborum}. | + {0:~ }| + | + ]]) + end) +end) diff --git a/test/functional/ui/syntax_conceal_spec.lua b/test/functional/ui/syntax_conceal_spec.lua index 28a104360d..e7a7004c1e 100644 --- a/test/functional/ui/syntax_conceal_spec.lua +++ b/test/functional/ui/syntax_conceal_spec.lua @@ -3,8 +3,6 @@ local Screen = require('test.functional.ui.screen') local clear, feed, command = helpers.clear, helpers.feed, helpers.command local insert = helpers.insert -if helpers.pending_win32(pending) then return end - describe('Screen', function() local screen diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index 042969357e..c6ddc78618 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -60,8 +60,7 @@ describe("'wildmenu'", function() command('set wildmenu wildmode=full') command('set scrollback=4') if iswin() then - if helpers.pending_win32(pending) then return end - -- feed([[:terminal 1,2,3,4,5 | foreach-object -process {echo $_; sleep 0.1}]]) + feed([[:terminal for /L \%I in (1,1,5000) do @(echo foo & echo foo & echo foo)<cr>]]) else feed([[:terminal for i in $(seq 1 5000); do printf 'foo\nfoo\nfoo\n'; sleep 0.1; done<cr>]]) end diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua index b70ef724b7..216ccb3744 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/viml/completion_spec.lua @@ -5,8 +5,6 @@ local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq local feed_command, source, expect = helpers.feed_command, helpers.source, helpers.expect local meths = helpers.meths -if helpers.pending_win32(pending) then return end - describe('completion', function() local screen @@ -61,7 +59,8 @@ describe('completion', function() it('returns expected dict in normal completion', function() feed('ifoo<ESC>o<C-x><C-n>') eq('foo', eval('getline(2)')) - eq({word = 'foo', abbr = '', menu = '', info = '', kind = ''}, + eq({word = 'foo', abbr = '', menu = '', + info = '', kind = '', user_data = ''}, eval('v:completed_item')) end) it('is readonly', function() @@ -86,13 +85,18 @@ describe('completion', function() 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'}] + \ {'word': 'word', 'abbr': 'abbr', 'menu': 'menu', + \ 'info': 'info', 'kind': 'kind'}] endfunction setlocal omnifunc=TestOmni ]]) @@ -109,7 +113,7 @@ describe('completion', function() {3:-- Omni completion (^O^N^P) }{4:match 1 of 2} | ]]) eq({word = 'foo', abbr = 'bar', menu = 'baz', - info = 'foobar', kind = 'foobaz'}, + info = 'foobar', kind = 'foobaz', user_data = ''}, eval('v:completed_item')) end) end) |