diff options
Diffstat (limited to 'test')
73 files changed, 2818 insertions, 525 deletions
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua index 7eb7ee73f9..440e93da0e 100644 --- a/test/functional/api/command_spec.lua +++ b/test/functional/api/command_spec.lua @@ -114,8 +114,8 @@ describe('nvim_create_user_command', function() ]] eq({ - args = [[this is a\ test]], - fargs = {"this", "is", "a test"}, + args = [[this\ is a\ test]], + fargs = {"this ", "is", "a test"}, bang = false, line1 = 1, line2 = 1, @@ -144,7 +144,7 @@ describe('nvim_create_user_command', function() count = 2, reg = "", }, exec_lua [=[ - vim.api.nvim_command([[CommandWithLuaCallback this is a\ test]]) + vim.api.nvim_command([[CommandWithLuaCallback this\ is a\ test]]) return result ]=]) @@ -326,7 +326,7 @@ describe('nvim_create_user_command', function() -- f-args doesn't split when command nargs is 1 or "?" exec_lua [[ result = {} - vim.api.nvim_create_user_command('CommandWithOneArg', function(opts) + vim.api.nvim_create_user_command('CommandWithOneOrNoArg', function(opts) result = opts end, { nargs = "?", @@ -366,7 +366,89 @@ describe('nvim_create_user_command', function() count = 2, reg = "", }, exec_lua [[ - vim.api.nvim_command('CommandWithOneArg hello I\'m one argument') + vim.api.nvim_command('CommandWithOneOrNoArg hello I\'m one argument') + return result + ]]) + + -- f-args is an empty table if no args were passed + eq({ + args = "", + fargs = {}, + bang = false, + line1 = 1, + line2 = 1, + mods = "", + smods = { + browse = false, + confirm = false, + emsg_silent = false, + hide = false, + keepalt = false, + keepjumps = false, + keepmarks = false, + keeppatterns = false, + lockmarks = false, + noautocmd = false, + noswapfile = false, + sandbox = false, + silent = false, + split = "", + tab = 0, + unsilent = false, + verbose = -1, + vertical = false, + }, + range = 0, + count = 2, + reg = "", + }, exec_lua [[ + vim.api.nvim_command('CommandWithOneOrNoArg') + return result + ]]) + + -- f-args is an empty table when the command nargs=0 + exec_lua [[ + result = {} + vim.api.nvim_create_user_command('CommandWithNoArgs', function(opts) + result = opts + end, { + nargs = 0, + bang = true, + count = 2, + }) + ]] + eq({ + args = "", + fargs = {}, + bang = false, + line1 = 1, + line2 = 1, + mods = "", + smods = { + browse = false, + confirm = false, + emsg_silent = false, + hide = false, + keepalt = false, + keepjumps = false, + keepmarks = false, + keeppatterns = false, + lockmarks = false, + noautocmd = false, + noswapfile = false, + sandbox = false, + silent = false, + split = "", + tab = 0, + unsilent = false, + verbose = -1, + vertical = false, + }, + range = 0, + count = 2, + reg = "", + }, exec_lua [[ + vim.cmd('CommandWithNoArgs') return result ]]) diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index c4197f0b3e..2730f7e23d 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -243,7 +243,7 @@ describe("API: set highlight", function() local function get_ns() local ns = meths.create_namespace('Test_set_hl') - meths._set_hl_ns(ns) + meths.set_hl_ns(ns) return ns end diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index fe623ff824..24d0b6da45 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -281,8 +281,8 @@ describe('API', function() ]]} end) - it('does\'t display messages when output=true', function() - local screen = Screen.new(40, 8) + it('doesn\'t display messages when output=true', function() + local screen = Screen.new(40, 6) screen:attach() screen:set_default_attr_ids({ [0] = {bold=true, foreground=Screen.colors.Blue}, @@ -294,9 +294,21 @@ describe('API', function() {0:~ }| {0:~ }| {0:~ }| + | + ]]} + exec([[ + func Print() + call nvim_exec('echo "hello"', v:true) + endfunc + ]]) + feed([[:echon 1 | call Print() | echon 5<CR>]]) + screen:expect{grid=[[ + ^ | {0:~ }| {0:~ }| - | + {0:~ }| + {0:~ }| + 15 | ]]} end) end) @@ -3656,6 +3668,55 @@ describe('API', function() :^ | ]]) end) + it('does not move cursor or change search history/pattern #19878 #19890', function() + meths.buf_set_lines(0, 0, -1, true, {'foo', 'bar', 'foo', 'bar'}) + eq({1, 0}, meths.win_get_cursor(0)) + eq('', funcs.getreg('/')) + eq('', funcs.histget('search')) + feed(':') -- call the API in cmdline mode to test whether it changes search history + eq({ + cmd = 'normal', + args = {'x'}, + bang = true, + range = {3, 4}, + count = -1, + reg = '', + addr = 'line', + magic = { + file = false, + bar = false, + }, + nargs = '+', + nextcmd = '', + mods = { + browse = false, + confirm = false, + emsg_silent = false, + filter = { + pattern = "", + force = false, + }, + hide = false, + keepalt = false, + keepjumps = false, + keepmarks = false, + keeppatterns = false, + lockmarks = false, + noautocmd = false, + noswapfile = false, + sandbox = false, + silent = false, + split = "", + tab = 0, + unsilent = false, + verbose = -1, + vertical = false, + } + }, meths.parse_cmd('+2;/bar/normal! x', {})) + eq({1, 0}, meths.win_get_cursor(0)) + eq('', funcs.getreg('/')) + eq('', funcs.histget('search')) + end) end) describe('nvim_cmd', function() it('works', function () @@ -3829,5 +3890,42 @@ describe('API', function() eq({'aa'}, meths.buf_get_lines(0, 0, 1, false)) assert_alive() end) + it("'make' command works when argument count isn't 1 #19696", function() + command('set makeprg=echo') + meths.cmd({ cmd = 'make' }, {}) + assert_alive() + meths.cmd({ cmd = 'make', args = { 'foo', 'bar' } }, {}) + assert_alive() + end) + it('doesn\'t display messages when output=true', function() + local screen = Screen.new(40, 6) + screen:attach() + screen:set_default_attr_ids({ + [0] = {bold=true, foreground=Screen.colors.Blue}, + }) + meths.cmd({cmd = 'echo', args = {[['hello']]}}, {output = true}) + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + exec([[ + func Print() + call nvim_cmd(#{cmd: 'echo', args: ['"hello"']}, #{output: v:true}) + endfunc + ]]) + feed([[:echon 1 | call Print() | echon 5<CR>]]) + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + 15 | + ]]} + end) end) end) diff --git a/test/functional/autocmd/winscrolled_spec.lua b/test/functional/autocmd/winscrolled_spec.lua index 5c1b758961..12b8e7c42d 100644 --- a/test/functional/autocmd/winscrolled_spec.lua +++ b/test/functional/autocmd/winscrolled_spec.lua @@ -61,6 +61,20 @@ describe('WinScrolled', function() eq(3, eval('g:scrolled')) end) + it('is triggered by scrolling on a long wrapped line #19968', function() + local height = meths.win_get_height(0) + local width = meths.win_get_width(0) + meths.buf_set_lines(0, 0, -1, true, {('foo'):rep(height * width)}) + meths.win_set_cursor(0, {1, height * width - 1}) + eq(0, eval('g:scrolled')) + feed('gj') + eq(1, eval('g:scrolled')) + feed('0') + eq(2, eval('g:scrolled')) + feed('$') + eq(3, eval('g:scrolled')) + end) + it('is triggered when the window scrolls in Insert mode', function() local height = meths.win_get_height(0) local lines = {} diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua index 4dba58dbfc..8cad7adfa6 100644 --- a/test/functional/core/exit_spec.lua +++ b/test/functional/core/exit_spec.lua @@ -3,6 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local assert_alive = helpers.assert_alive local command = helpers.command local feed_command = helpers.feed_command +local feed = helpers.feed local eval = helpers.eval local eq = helpers.eq local run = helpers.run @@ -36,11 +37,12 @@ describe('v:exiting', function() end run(on_request, nil, on_setup) end) - it('is 0 on exit from ex-mode involving try-catch', function() + it('is 0 on exit from Ex mode involving try-catch vim-patch:8.0.0184', 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') + feed('gQ') + feed_command('try', 'call NoFunction()', 'catch', 'echo "bye"', 'endtry', 'quit') end local function on_request() eq(0, eval('v:exiting')) diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 04fbb807be..02ff18bdda 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -21,6 +21,7 @@ local nvim_set = helpers.nvim_set local expect_twostreams = helpers.expect_twostreams local expect_msg_seq = helpers.expect_msg_seq local pcall_err = helpers.pcall_err +local matches = helpers.matches local Screen = require('test.functional.ui.screen') describe('jobs', function() @@ -229,8 +230,8 @@ describe('jobs', function() local dir = 'Xtest_not_executable_dir' mkdir(dir) funcs.setfperm(dir, 'rw-------') - eq('Vim(call):E475: Invalid argument: expected valid directory', - pcall_err(nvim, 'command', "call jobstart('pwd', {'cwd': '"..dir.."'})")) + matches('^Vim%(call%):E903: Process failed to start: permission denied: .*', + pcall_err(nvim, 'command', "call jobstart(['pwd'], {'cwd': '"..dir.."'})")) rmdir(dir) end) @@ -690,8 +691,8 @@ describe('jobs', function() -- jobstart() shares its v:servername with the child via $NVIM. eq('NVIM='..addr, get_env_in_child_job('NVIM')) -- $NVIM_LISTEN_ADDRESS is unset by server_init in the child. - eq('NVIM_LISTEN_ADDRESS=null', get_env_in_child_job('NVIM_LISTEN_ADDRESS')) - eq('NVIM_LISTEN_ADDRESS=null', get_env_in_child_job('NVIM_LISTEN_ADDRESS', + eq('NVIM_LISTEN_ADDRESS=v:null', get_env_in_child_job('NVIM_LISTEN_ADDRESS')) + eq('NVIM_LISTEN_ADDRESS=v:null', get_env_in_child_job('NVIM_LISTEN_ADDRESS', { NVIM_LISTEN_ADDRESS='Xtest_jobstart_env' })) -- User can explicitly set $NVIM_LOG_FILE, $VIM, $VIMRUNTIME. eq('NVIM_LOG_FILE=Xtest_jobstart_env', diff --git a/test/functional/editor/K_spec.lua b/test/functional/editor/K_spec.lua index 8ad81ac3d6..3b5580540f 100644 --- a/test/functional/editor/K_spec.lua +++ b/test/functional/editor/K_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) -local eq, clear, eval, feed, retry = - helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.retry +local eq, clear, eval, feed, meths, retry = + helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.meths, helpers.retry describe('K', function() local test_file = 'K_spec_out' @@ -58,4 +58,11 @@ describe('K', function() helpers.neq(bufnr, eval('bufnr()')) end) + it('empty string falls back to :help #19298', function() + meths.set_option('keywordprg', '') + meths.buf_set_lines(0, 0, -1, true, {'doesnotexist'}) + feed('K') + eq('E149: Sorry, no help for doesnotexist', meths.get_vvar('errmsg')) + end) + end) diff --git a/test/functional/editor/ctrl_c_spec.lua b/test/functional/editor/ctrl_c_spec.lua index 60131bf2a4..4548e1aa34 100644 --- a/test/functional/editor/ctrl_c_spec.lua +++ b/test/functional/editor/ctrl_c_spec.lua @@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed, source = helpers.clear, helpers.feed, helpers.source local command = helpers.command +local poke_eventloop = helpers.poke_eventloop local sleep = helpers.sleep describe("CTRL-C (mapped)", function() @@ -57,11 +58,9 @@ describe("CTRL-C (mapped)", function() it('interrupts :sleep', function() command('nnoremap <C-C> <Nop>') feed(':sleep 100<CR>') - -- wait for :sleep to start - sleep(10) + poke_eventloop() -- wait for :sleep to start feed('foo<C-C>') - -- wait for input buffer to be flushed - sleep(10) + poke_eventloop() -- wait for input buffer to be flushed feed('i') screen:expect([[ ^ | @@ -77,10 +76,9 @@ describe("CTRL-C (mapped)", function() command('nnoremap <C-C> <Nop>') command('nmap <F2> <Ignore><F2>') feed('<F2>') - sleep(10) + sleep(10) -- wait for the key to enter typeahead feed('foo<C-C>') - -- wait for input buffer to be flushed - sleep(10) + poke_eventloop() -- wait for input buffer to be flushed feed('i') screen:expect([[ ^ | diff --git a/test/functional/editor/mode_insert_spec.lua b/test/functional/editor/mode_insert_spec.lua index e3d3cdbd85..cd51a65be3 100644 --- a/test/functional/editor/mode_insert_spec.lua +++ b/test/functional/editor/mode_insert_spec.lua @@ -6,12 +6,20 @@ local expect = helpers.expect local command = helpers.command local eq = helpers.eq local eval = helpers.eval +local curbuf_contents = helpers.curbuf_contents describe('insert-mode', function() before_each(function() clear() end) + it('indents only once after "!" keys #12894', function() + command('let counter = []') + command('set indentexpr=len(add(counter,0))') + feed('i<C-F>x') + eq(' x', curbuf_contents()) + end) + it('CTRL-@', function() -- Inserts last-inserted text, leaves insert-mode. insert('hello') diff --git a/test/functional/ex_cmds/normal_spec.lua b/test/functional/ex_cmds/normal_spec.lua index f6e7dd2b3a..009f1d6516 100644 --- a/test/functional/ex_cmds/normal_spec.lua +++ b/test/functional/ex_cmds/normal_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local command = helpers.command +local funcs = helpers.funcs local feed = helpers.feed local expect = helpers.expect local eq = helpers.eq @@ -8,20 +9,30 @@ local eval = helpers.eval before_each(clear) -describe(':normal', function() +describe(':normal!', function() it('can get out of Insert mode if called from Ex mode #17924', function() feed('gQnormal! Ifoo<CR>') expect('foo') end) - it('normal! does not execute command in Ex mode when running out of characters', function() + it('does not execute command in Ex mode when running out of characters', function() command('let g:var = 0') command('normal! gQlet g:var = 1') eq(0, eval('g:var')) end) - it('normal! gQinsert does not hang #17980', function() + it('gQinsert does not hang #17980', function() command('normal! gQinsert') expect('') end) + + it('can stop Visual mode without closing cmdwin vim-patch:9.0.0234', function() + feed('q:') + feed('v') + eq('v', funcs.mode(1)) + eq(':', funcs.getcmdwintype()) + command('normal! \027') + eq('n', funcs.mode(1)) + eq(':', funcs.getcmdwintype()) + end) end) diff --git a/test/functional/ex_cmds/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua index 003ab64dd4..5f87c3cdd9 100644 --- a/test/functional/ex_cmds/oldfiles_spec.lua +++ b/test/functional/ex_cmds/oldfiles_spec.lua @@ -29,7 +29,6 @@ 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') @@ -37,7 +36,7 @@ describe(':oldfiles', function() local oldfiles = helpers.meths.get_vvar('oldfiles') feed_command('oldfiles') screen:expect([[ - testfile2 | + | 1: ]].. add_padding(oldfiles[1]) ..[[ | 2: ]].. add_padding(oldfiles[2]) ..[[ | | diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua index 13a40fcc53..cd1f43f9fd 100644 --- a/test/functional/ex_cmds/source_spec.lua +++ b/test/functional/ex_cmds/source_spec.lua @@ -13,6 +13,10 @@ local exec_lua = helpers.exec_lua local eval = helpers.eval local exec_capture = helpers.exec_capture local neq = helpers.neq +local matches = helpers.matches +local iswin = helpers.iswin +local mkdir = helpers.mkdir +local rmdir = helpers.rmdir describe(':source', function() before_each(function() @@ -39,6 +43,47 @@ describe(':source', function() os.remove(test_file) end) + it("changing 'shellslash' changes the result of expand()", function() + if not iswin() then + pending("'shellslash' only works on Windows") + return + end + meths.set_option('shellslash', false) + mkdir('Xshellslash') + + write_file([[Xshellslash/Xstack.vim]], [[ + let g:stack1 = expand('<stack>') + set shellslash + let g:stack2 = expand('<stack>') + set noshellslash + let g:stack3 = expand('<stack>') + ]]) + + for _ = 1, 2 do + command([[source Xshellslash/Xstack.vim]]) + matches([[Xshellslash\Xstack%.vim]], meths.get_var('stack1')) + matches([[Xshellslash/Xstack%.vim]], meths.get_var('stack2')) + matches([[Xshellslash\Xstack%.vim]], meths.get_var('stack3')) + end + + write_file([[Xshellslash/Xstack.lua]], [[ + vim.g.stack1 = vim.fn.expand('<stack>') + vim.o.shellslash = true + vim.g.stack2 = vim.fn.expand('<stack>') + vim.o.shellslash = false + vim.g.stack3 = vim.fn.expand('<stack>') + ]]) + + for _ = 1, 2 do + command([[source Xshellslash/Xstack.lua]]) + matches([[Xshellslash\Xstack%.lua]], meths.get_var('stack1')) + matches([[Xshellslash/Xstack%.lua]], meths.get_var('stack2')) + matches([[Xshellslash\Xstack%.lua]], meths.get_var('stack3')) + end + + rmdir('Xshellslash') + end) + it('current buffer', function() insert([[ let a = 2 @@ -117,11 +162,20 @@ describe(':source', function() it('can source lua files', function() local test_file = 'test.lua' - write_file (test_file, [[vim.g.sourced_lua = 1]]) - - exec('source ' .. test_file) + write_file(test_file, [[ + vim.g.sourced_lua = 1 + vim.g.sfile_value = vim.fn.expand('<sfile>') + vim.g.stack_value = vim.fn.expand('<stack>') + vim.g.script_value = vim.fn.expand('<script>') + ]]) + command('set shellslash') + command('source ' .. test_file) eq(1, eval('g:sourced_lua')) + matches([[/test%.lua$]], meths.get_var('sfile_value')) + matches([[/test%.lua$]], meths.get_var('stack_value')) + matches([[/test%.lua$]], meths.get_var('script_value')) + os.remove(test_file) end) @@ -153,13 +207,16 @@ describe(':source', function() it('can source current lua buffer without argument', function() local test_file = 'test.lua' - write_file (test_file, [[ + write_file(test_file, [[ vim.g.c = 10 vim.g.c = 11 vim.g.c = 12 a = [=[ \ 1 "\ 2]=] + vim.g.sfile_value = vim.fn.expand('<sfile>') + vim.g.stack_value = vim.fn.expand('<stack>') + vim.g.script_value = vim.fn.expand('<script>') ]]) command('edit '..test_file) @@ -167,6 +224,10 @@ describe(':source', function() eq(12, eval('g:c')) eq(' \\ 1\n "\\ 2', exec_lua('return _G.a')) + eq(':source (no file)', meths.get_var('sfile_value')) + eq(':source (no file)', meths.get_var('stack_value')) + eq(':source (no file)', meths.get_var('script_value')) + os.remove(test_file) end) diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua index 4d984af41e..69404039ff 100644 --- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua +++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua @@ -1,8 +1,8 @@ local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers')(after_each) local lfs = require('lfs') -local eq, eval, expect, source = - helpers.eq, helpers.eval, helpers.expect, helpers.source +local eq, eval, expect, exec = + helpers.eq, helpers.eval, helpers.expect, helpers.exec local assert_alive = helpers.assert_alive local clear = helpers.clear local command = helpers.command @@ -10,6 +10,8 @@ local feed = helpers.feed local nvim_prog = helpers.nvim_prog local ok = helpers.ok local rmdir = helpers.rmdir +local new_argv = helpers.new_argv +local pesc = helpers.pesc local os_kill = helpers.os_kill local set_session = helpers.set_session local spawn = helpers.spawn @@ -55,11 +57,11 @@ describe(':preserve', function() set swapfile fileformat=unix undolevels=-1 ]] - source(init) + exec(init) command('edit! '..testfile) feed('isometext<esc>') command('preserve') - source('redir => g:swapname | silent swapname | redir END') + exec('redir => g:swapname | silent swapname | redir END') local swappath1 = eval('g:swapname') @@ -69,12 +71,12 @@ describe(':preserve', function() true) set_session(nvim2) - source(init) + exec(init) -- Use the "SwapExists" event to choose the (R)ecover choice at the dialog. command('autocmd SwapExists * let v:swapchoice = "r"') command('silent edit! '..testfile) - source('redir => g:swapname | silent swapname | redir END') + exec('redir => g:swapname | silent swapname | redir END') local swappath2 = eval('g:swapname') @@ -92,25 +94,28 @@ end) describe('swapfile detection', function() local swapdir = lfs.currentdir()..'/Xtest_swapdialog_dir' + local nvim0 + -- Put swapdir at the start of the 'directory' list. #1836 + -- Note: `set swapfile` *must* go after `set directory`: otherwise it may + -- attempt to create a swapfile in different directory. + local init = [[ + set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[// + set swapfile fileformat=unix undolevels=-1 hidden + ]] before_each(function() - clear() + nvim0 = spawn(new_argv()) + set_session(nvim0) rmdir(swapdir) lfs.mkdir(swapdir) end) after_each(function() + set_session(nvim0) command('%bwipeout!') rmdir(swapdir) end) it('always show swapfile dialog #8840 #9027', function() local testfile = 'Xtest_swapdialog_file1' - -- Put swapdir at the start of the 'directory' list. #1836 - -- Note: `set swapfile` *must* go after `set directory`: otherwise it may - -- attempt to create a swapfile in different directory. - local init = [[ - set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[// - set swapfile fileformat=unix undolevels=-1 hidden - ]] local expected_no_dialog = '^'..(' '):rep(256)..'|\n' for _=1,37 do @@ -119,19 +124,17 @@ describe('swapfile detection', function() expected_no_dialog = expected_no_dialog..testfile..(' '):rep(216)..'0,0-1 All|\n' expected_no_dialog = expected_no_dialog..(' '):rep(256)..'|\n' - source(init) + exec(init) command('edit! '..testfile) feed('isometext<esc>') command('preserve') - os_kill(eval('getpid()')) -- Start another Nvim instance. - local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'}, - true) + local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'}, true, nil, true) set_session(nvim2) local screen2 = Screen.new(256, 40) screen2:attach() - source(init) + exec(init) -- With shortmess+=F command('set shortmess+=F') @@ -176,5 +179,88 @@ describe('swapfile detection', function() } }) feed('<cr>') + + nvim2:close() + end) + + -- oldtest: Test_swap_prompt_splitwin() + it('selecting "q" in the attention prompt', function() + exec(init) + command('edit Xfile1') + command('preserve') -- should help to make sure the swap file exists + + local screen = Screen.new(75, 18) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg + }) + + local nvim1 = spawn(new_argv(), true, nil, true) + set_session(nvim1) + screen:attach() + exec(init) + feed(':split Xfile1\n') + screen:expect({ + any = pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^') + }) + feed('q') + feed(':<CR>') + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + : | + ]]) + nvim1:close() + + local nvim2 = spawn(new_argv(), true, nil, true) + set_session(nvim2) + screen:attach() + exec(init) + command('set more') + command('au bufadd * let foo_w = wincol()') + feed(':e Xfile1<CR>') + screen:expect({any = pesc('{1:-- More --}^')}) + feed('<Space>') + screen:expect({ + any = pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^') + }) + feed('q') + command([[echo 'hello']]) + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + hello | + ]]) + nvim2:close() end) end) diff --git a/test/functional/ex_cmds/wincmd_spec.lua b/test/functional/ex_cmds/wincmd_spec.lua new file mode 100644 index 0000000000..b1f174f445 --- /dev/null +++ b/test/functional/ex_cmds/wincmd_spec.lua @@ -0,0 +1,13 @@ +local helpers = require("test.functional.helpers")(after_each) +local clear = helpers.clear +local eq = helpers.eq +local funcs = helpers.funcs +local command = helpers.command + +it(':wincmd accepts a count', function() + clear() + command('vsplit') + eq(1, funcs.winnr()) + command('wincmd 2 w') + eq(2, funcs.winnr()) +end) diff --git a/test/functional/fixtures/compdir/file1 b/test/functional/fixtures/wildpum/Xdir/XdirA/XdirB/XfileC index e69de29bb2..e69de29bb2 100644 --- a/test/functional/fixtures/compdir/file1 +++ b/test/functional/fixtures/wildpum/Xdir/XdirA/XdirB/XfileC diff --git a/test/functional/fixtures/compdir/file2 b/test/functional/fixtures/wildpum/Xdir/XdirA/XfileB index e69de29bb2..e69de29bb2 100644 --- a/test/functional/fixtures/compdir/file2 +++ b/test/functional/fixtures/wildpum/Xdir/XdirA/XfileB diff --git a/test/functional/fixtures/wildpum/Xdir/XfileA b/test/functional/fixtures/wildpum/Xdir/XfileA new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/functional/fixtures/wildpum/Xdir/XfileA diff --git a/test/functional/fixtures/wildpum/compdir/file1 b/test/functional/fixtures/wildpum/compdir/file1 new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/functional/fixtures/wildpum/compdir/file1 diff --git a/test/functional/fixtures/wildpum/compdir/file2 b/test/functional/fixtures/wildpum/compdir/file2 new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/functional/fixtures/wildpum/compdir/file2 diff --git a/test/functional/fixtures/wildpum/あいう/123 b/test/functional/fixtures/wildpum/あいう/123 new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/functional/fixtures/wildpum/あいう/123 diff --git a/test/functional/fixtures/wildpum/あいう/abc b/test/functional/fixtures/wildpum/あいう/abc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/functional/fixtures/wildpum/あいう/abc diff --git a/test/functional/fixtures/wildpum/あいう/xyz b/test/functional/fixtures/wildpum/あいう/xyz new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/functional/fixtures/wildpum/あいう/xyz diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 8c5a60657a..93fb0f245e 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -244,10 +244,12 @@ function module.run_session(lsession, request_cb, notification_cb, setup_cb, tim last_error = nil error(err) end + + return session.eof_err end function module.run(request_cb, notification_cb, setup_cb, timeout) - module.run_session(session, request_cb, notification_cb, setup_cb, timeout) + return module.run_session(session, request_cb, notification_cb, setup_cb, timeout) end function module.stop() @@ -759,6 +761,7 @@ function module.pending_c_parser(pending_fn) pending_fn 'no C parser, skipping' return true end + module.exec_lua [[vim._ts_remove_language 'c']] return false end diff --git a/test/functional/legacy/arglist_spec.lua b/test/functional/legacy/arglist_spec.lua index f90da16d7b..4d9e88c446 100644 --- a/test/functional/legacy/arglist_spec.lua +++ b/test/functional/legacy/arglist_spec.lua @@ -276,6 +276,6 @@ describe('argument list commands', function() 2 more files to edit. Quit anyway? | [Y]es, (N)o: ^ | ]]) - expect_exit(100, feed, 'Y') + expect_exit(1000, feed, 'Y') end) end) diff --git a/test/functional/legacy/buffer_spec.lua b/test/functional/legacy/buffer_spec.lua new file mode 100644 index 0000000000..acaa9a51f1 --- /dev/null +++ b/test/functional/legacy/buffer_spec.lua @@ -0,0 +1,59 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear, source = helpers.clear, helpers.source +local call, eq, meths = helpers.call, helpers.eq, helpers.meths + +local function expected_empty() + eq({}, meths.get_vvar('errors')) +end + +describe('buffer', function() + before_each(function() + clear() + meths.ui_attach(80, 24, {}) + meths.set_option('hidden', false) + end) + + it('deleting a modified buffer with :confirm', function() + source([[ + func Test_bdel_with_confirm() + new + call setline(1, 'test') + call assert_fails('bdel', 'E89:') + call nvim_input('c') + confirm bdel + call assert_equal(2, winnr('$')) + call assert_equal(1, &modified) + call nvim_input('n') + confirm bdel + call assert_equal(1, winnr('$')) + endfunc + ]]) + call('Test_bdel_with_confirm') + expected_empty() + end) + + it('editing another buffer from a modified buffer with :confirm', function() + source([[ + func Test_goto_buf_with_confirm() + new Xfile + enew + call setline(1, 'test') + call assert_fails('b Xfile', 'E37:') + call nvim_input('c') + call assert_fails('confirm b Xfile', 'E37:') + call assert_equal(1, &modified) + call assert_equal('', @%) + call nvim_input('y') + call assert_fails('confirm b Xfile', 'E37:') + call assert_equal(1, &modified) + call assert_equal('', @%) + call nvim_input('n') + confirm b Xfile + call assert_equal('Xfile', @%) + close! + endfunc + ]]) + call('Test_goto_buf_with_confirm') + expected_empty() + end) +end) diff --git a/test/functional/legacy/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua index cf02636890..99d2d0f30e 100644 --- a/test/functional/legacy/cmdline_spec.lua +++ b/test/functional/legacy/cmdline_spec.lua @@ -1,9 +1,11 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear = helpers.clear +local command = helpers.command local feed = helpers.feed local feed_command = helpers.feed_command local exec = helpers.exec +local pesc = helpers.pesc describe('cmdline', function() before_each(clear) @@ -18,8 +20,6 @@ describe('cmdline', function() [3] = {reverse = true}; [4] = {bold = true, foreground = Screen.colors.Blue1}; } - -- TODO(bfredl): redraw with tabs is severly broken. fix it - feed_command [[ set display-=msgsep ]] feed_command([[call setline(1, range(30))]]) screen:expect([[ @@ -60,7 +60,7 @@ describe('cmdline', function() {4:~ }| | | - :tabnew | + | ]]} feed [[gt]] @@ -141,3 +141,81 @@ describe('cmdline', function() ]]) end) end) + +describe('cmdwin', function() + before_each(clear) + + -- oldtest: Test_cmdwin_interrupted() + it('still uses a new buffer when interrupting more prompt on open', function() + local screen = Screen.new(30, 16) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {bold = true, reverse = true}, -- StatusLine + [2] = {reverse = true}, -- StatusLineNC + [3] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg + [4] = {bold = true}, -- ModeMsg + }) + screen:attach() + command('set more') + command('autocmd WinNew * highlight') + feed('q:') + screen:expect({any = pesc('{3:-- More --}^')}) + feed('q') + screen:expect([[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:[No Name] }| + {0::}^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {1:[Command Line] }| + | + ]]) + feed([[aecho 'done']]) + screen:expect([[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:[No Name] }| + {0::}echo 'done'^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {1:[Command Line] }| + {4:-- INSERT --} | + ]]) + feed('<CR>') + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + done | + ]]) + end) +end) diff --git a/test/functional/legacy/ex_mode_spec.lua b/test/functional/legacy/ex_mode_spec.lua index a8f54c6939..f21c47e175 100644 --- a/test/functional/legacy/ex_mode_spec.lua +++ b/test/functional/legacy/ex_mode_spec.lua @@ -6,7 +6,7 @@ local eq = helpers.eq local eval = helpers.eval local feed = helpers.feed local meths = helpers.meths -local sleep = helpers.sleep +local poke_eventloop = helpers.poke_eventloop before_each(clear) @@ -143,7 +143,7 @@ describe('Ex mode', function() ^ | ]]) feed('<C-C>') - sleep(10) -- Wait for input to be flushed + poke_eventloop() -- Wait for input to be flushed feed('foo<CR>') screen:expect([[ Entering Ex mode. Type "visual" to go to Normal mode. | diff --git a/test/functional/legacy/excmd_spec.lua b/test/functional/legacy/excmd_spec.lua index 65957d85de..ece88d26bd 100644 --- a/test/functional/legacy/excmd_spec.lua +++ b/test/functional/legacy/excmd_spec.lua @@ -94,7 +94,7 @@ describe(':confirm command dialog', function() {3:Save changes to "Xbar"?} | {3:[Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: }^ | ]]) - expect_exit(100, feed, 'A') + expect_exit(1000, feed, 'A') eq('foo2\n', read_file('Xfoo')) eq('bar2\n', read_file('Xbar')) @@ -132,7 +132,7 @@ describe(':confirm command dialog', function() {3:Save changes to "Xbar"?} | {3:[Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: }^ | ]]) - expect_exit(100, feed, 'D') + expect_exit(1000, feed, 'D') eq('foo2\n', read_file('Xfoo')) eq('bar2\n', read_file('Xbar')) @@ -193,7 +193,7 @@ describe(':confirm command dialog', function() {3:Save changes to "Xfoo"?} | {3:[Y]es, (N)o, (C)ancel: }^ | ]]) - expect_exit(100, feed, 'Y') + expect_exit(1000, feed, 'Y') eq('foo4\n', read_file('Xfoo')) eq('bar2\n', read_file('Xbar')) diff --git a/test/functional/legacy/filechanged_spec.lua b/test/functional/legacy/filechanged_spec.lua index ecb861098c..1f23528d61 100644 --- a/test/functional/legacy/filechanged_spec.lua +++ b/test/functional/legacy/filechanged_spec.lua @@ -67,6 +67,15 @@ describe('file changed dialog', function() call assert_equal(1, line('$')) call assert_equal('new line', getline(1)) + " File created after starting to edit it + call delete('Xchanged_d') + new Xchanged_d + call writefile(['one'], 'Xchanged_d') + call nvim_input('L') + checktime Xchanged_d + call assert_equal(['one'], getline(1, '$')) + close! + bwipe! call delete('Xchanged_d') endfunc diff --git a/test/functional/legacy/gf_spec.lua b/test/functional/legacy/gf_spec.lua new file mode 100644 index 0000000000..f1b1790ba1 --- /dev/null +++ b/test/functional/legacy/gf_spec.lua @@ -0,0 +1,15 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq +local pcall_err = helpers.pcall_err + +describe('gf', function() + before_each(clear) + + it('is not allowed when buffer is locked', function() + command('au OptionSet diff norm! gf') + command([[call setline(1, ['Xfile1', 'line2', 'line3', 'line4'])]]) + eq('Vim(normal):E788: Not allowed to edit another buffer now', pcall_err(command, 'diffthis')) + end) +end) diff --git a/test/functional/legacy/global_spec.lua b/test/functional/legacy/global_spec.lua index 9f4528530c..ff02c41e6c 100644 --- a/test/functional/legacy/global_spec.lua +++ b/test/functional/legacy/global_spec.lua @@ -3,7 +3,7 @@ local Screen = require('test.functional.ui.screen') local clear = helpers.clear local exec = helpers.exec local feed = helpers.feed -local sleep = helpers.sleep +local poke_eventloop = helpers.poke_eventloop before_each(clear) @@ -24,7 +24,7 @@ describe(':global', function() ]]) feed(':g/foo/norm :<C-V>;<CR>') - sleep(10) -- Wait for :sleep to start + poke_eventloop() -- Wait for :sleep to start feed('<C-C>') screen:expect([[ ^foo | @@ -37,7 +37,7 @@ describe(':global', function() -- Also test in Ex mode feed('gQg/foo/norm :<C-V>;<CR>') - sleep(10) -- Wait for :sleep to start + poke_eventloop() -- Wait for :sleep to start feed('<C-C>') screen:expect([[ {0: }| diff --git a/test/functional/legacy/mapping_spec.lua b/test/functional/legacy/mapping_spec.lua index 456acc12b5..c1f23ab0a6 100644 --- a/test/functional/legacy/mapping_spec.lua +++ b/test/functional/legacy/mapping_spec.lua @@ -131,11 +131,11 @@ describe('mapping', function() command('set selectmode=mouse') command('nnoremap <LeftDrag> <LeftDrag><Cmd><CR>') - sleep(10) + poke_eventloop() meths.input_mouse('left', 'press', '', 0, 0, 0) - sleep(10) + poke_eventloop() meths.input_mouse('left', 'drag', '', 0, 0, 1) - sleep(10) + poke_eventloop() eq('s', eval('mode()')) end) @@ -144,22 +144,22 @@ describe('mapping', function() command('inoremap <LeftDrag> <LeftDrag><Cmd>let g:dragged = 1<CR>') feed('i') - sleep(10) + poke_eventloop() meths.input_mouse('left', 'press', '', 0, 0, 0) - sleep(10) + poke_eventloop() meths.input_mouse('left', 'drag', '', 0, 0, 1) - sleep(10) + poke_eventloop() eq(1, eval('g:dragged')) eq('v', eval('mode()')) feed([[<C-\><C-N>]]) command([[inoremap <LeftDrag> <LeftDrag><C-\><C-N>]]) feed('i') - sleep(10) + poke_eventloop() meths.input_mouse('left', 'press', '', 0, 0, 0) - sleep(10) + poke_eventloop() meths.input_mouse('left', 'drag', '', 0, 0, 1) - sleep(10) + poke_eventloop() eq('n', eval('mode()')) end) diff --git a/test/functional/legacy/messages_spec.lua b/test/functional/legacy/messages_spec.lua index 51c2406933..159cf7a551 100644 --- a/test/functional/legacy/messages_spec.lua +++ b/test/functional/legacy/messages_spec.lua @@ -337,6 +337,95 @@ describe('messages', function() end) end) + describe('mode is cleared when', function() + before_each(function() + screen = Screen.new(40, 6) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [2] = {bold = true}, -- ModeMsg + [3] = {bold = true, reverse=true}, -- StatusLine + }) + screen:attach() + end) + + -- oldtest: Test_mode_message_at_leaving_insert_by_ctrl_c() + it('leaving Insert mode with Ctrl-C vim-patch:8.1.1189', function() + exec([[ + func StatusLine() abort + return "" + endfunc + set statusline=%!StatusLine() + set laststatus=2 + ]]) + feed('i') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {3: }| + {2:-- INSERT --} | + ]]) + feed('<C-C>') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {3: }| + | + ]]) + end) + + -- oldtest: Test_mode_message_at_leaving_insert_with_esc_mapped() + it('leaving Insert mode with ESC in the middle of a mapping vim-patch:8.1.1192', function() + exec([[ + set laststatus=2 + inoremap <Esc> <Esc>00 + ]]) + feed('i') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {3:[No Name] }| + {2:-- INSERT --} | + ]]) + feed('<Esc>') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {3:[No Name] }| + | + ]]) + end) + + -- oldtest: Test_mode_updated_after_ctrl_c() + it('pressing Ctrl-C in i_CTRL-O', function() + feed('i<C-O>') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- (insert) --} | + ]]) + feed('<C-C>') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + end) + end) + -- oldtest: Test_ask_yesno() it('y/n prompt works', function() screen = Screen.new(75, 6) diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua index f9647f5b6a..4226bcebac 100644 --- a/test/functional/lua/diagnostic_spec.lua +++ b/test/functional/lua/diagnostic_spec.lua @@ -128,6 +128,28 @@ describe('vim.diagnostic', function() eq('Diagnostic #1', result[1].message) end) + it('removes diagnostics from the cache when a buffer is removed', function() + eq(2, exec_lua [[ + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + local other_bufnr = vim.fn.bufadd('test | test') + local lines = vim.api.nvim_buf_get_lines(diagnostic_bufnr, 0, -1, true) + vim.api.nvim_buf_set_lines(other_bufnr, 0, 1, false, lines) + vim.cmd('bunload! ' .. other_bufnr) + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 1, 1, 1, 1), + make_error('Diagnostic #2', 2, 1, 2, 1), + }) + vim.diagnostic.set(diagnostic_ns, other_bufnr, { + make_error('Diagnostic #3', 3, 1, 3, 1), + }) + vim.api.nvim_set_current_buf(other_bufnr) + vim.opt_local.buflisted = true + vim.cmd('bwipeout!') + return #vim.diagnostic.get() + ]]) + end) + it('resolves buffer number 0 to the current buffer', function() eq(2, exec_lua [[ vim.api.nvim_set_current_buf(diagnostic_bufnr) diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index 1322155595..9f313eab9e 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -467,7 +467,6 @@ describe('v:lua', function() end end end - vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.mymod.omni') ]]) end) @@ -515,6 +514,7 @@ describe('v:lua', function() [5] = {bold = true, foreground = Screen.colors.SeaGreen4}, }) screen:attach() + meths.buf_set_option(0, 'omnifunc', 'v:lua.mymod.omni') feed('isome st<c-x><c-o>') screen:expect{grid=[[ some stuff^ | @@ -526,6 +526,9 @@ describe('v:lua', function() {1:~ }| {4:-- Omni completion (^O^N^P) }{5:match 1 of 3} | ]]} + meths.set_option('operatorfunc', 'v:lua.mymod.noisy') + feed('<Esc>g@g@') + eq("hey line", meths.get_current_line()) end) it('supports packages', function() diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua index 9b51af1eec..32c1615a45 100644 --- a/test/functional/lua/overrides_spec.lua +++ b/test/functional/lua/overrides_spec.lua @@ -144,13 +144,14 @@ describe('debug.debug', function() before_each(function() screen = Screen.new() screen:attach() - screen:set_default_attr_ids({ - [0] = {bold=true, foreground=255}, - E = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, - cr = {bold = true, foreground = Screen.colors.SeaGreen4}, - }) - command("set display-=msgsep") + screen:set_default_attr_ids { + [0] = {bold=true, foreground=255}; + [1] = {bold = true, reverse = true}; + E = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}; + cr = {bold = true, foreground = Screen.colors.SeaGreen4}; + } end) + it('works', function() command([[lua function Test(a) @@ -160,9 +161,8 @@ describe('debug.debug', function() end ]]) feed(':lua Test()\n') - screen:expect([[ - {0:~ }| - {0:~ }| + screen:expect{grid=[[ + | {0:~ }| {0:~ }| {0:~ }| @@ -173,11 +173,13 @@ describe('debug.debug', function() {0:~ }| {0:~ }| {0:~ }| + {1: }| nil | lua_debug> ^ | - ]]) + ]]} feed('print("TEST")\n') screen:expect([[ + | {0:~ }| {0:~ }| {0:~ }| @@ -186,8 +188,7 @@ describe('debug.debug', function() {0:~ }| {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| + {1: }| nil | lua_debug> print("TEST") | TEST | @@ -195,10 +196,10 @@ describe('debug.debug', function() ]]) feed('<C-c>') screen:expect{grid=[[ + | {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| + {1: }| nil | lua_debug> print("TEST") | TEST | @@ -212,6 +213,7 @@ describe('debug.debug', function() ]]} feed('<C-l>:lua Test()\n') screen:expect([[ + | {0:~ }| {0:~ }| {0:~ }| @@ -222,19 +224,18 @@ describe('debug.debug', function() {0:~ }| {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| + {1: }| nil | lua_debug> ^ | ]]) feed('\n') screen:expect{grid=[[ + | {0:~ }| {0:~ }| {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| + {1: }| nil | lua_debug> | {E:E5108: Error executing lua [string ":lua"]:5: attempt}| @@ -268,6 +269,7 @@ describe('debug.debug', function() feed("conttt<cr>") -- misspelled cont; invalid syntax screen:expect{grid=[[ + | {0:~ }| {0:~ }| {0:~ }| @@ -276,8 +278,7 @@ describe('debug.debug', function() {0:~ }| {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| + {1: }| lua_debug> conttt | {E:E5115: Error while loading debug string: (debug comma}| {E:nd):1: '=' expected near '<eof>'} | @@ -286,14 +287,14 @@ describe('debug.debug', function() feed("cont<cr>") -- exactly "cont", exit now screen:expect{grid=[[ + | {0:~ }| {0:~ }| {0:~ }| {0:~ }| {0:~ }| {0:~ }| - {0:~ }| - {0:~ }| + {1: }| lua_debug> conttt | {E:E5115: Error while loading debug string: (debug comma}| {E:nd):1: '=' expected near '<eof>'} | diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 2b249b7a69..f2fb661b70 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -2721,6 +2721,39 @@ describe('lua stdlib', function() ]] end) end) + + describe('vim.iconv', function() + it('can convert strings', function() + eq('hello', exec_lua[[ + return vim.iconv('hello', 'latin1', 'utf-8') + ]]) + end) + + it('can validate arguments', function() + eq({false, 'Expected at least 3 arguments'}, exec_lua[[ + return {pcall(vim.iconv, 'hello')} + ]]) + + eq({false, 'bad argument #3 to \'?\' (expected string)'}, exec_lua[[ + return {pcall(vim.iconv, 'hello', 'utf-8', true)} + ]]) + end) + + it('can handle bad encodings', function() + eq(NIL, exec_lua[[ + return vim.iconv('hello', 'foo', 'bar') + ]]) + end) + + it('can handle strings with NUL bytes', function() + eq(7, exec_lua[[ + local a = string.char(97, 98, 99, 0, 100, 101, 102) -- abc\0def + return string.len(vim.iconv(a, 'latin1', 'utf-8')) + ]]) + end) + + end) + end) describe('lua: builtin modules', function() diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index 9244ca0974..4e2f2ab63e 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -161,6 +161,36 @@ describe('startup defaults', function() ~ |~ | | ]]) + + -- change "vert" character to single-cell + funcs.setcellwidths({{0x2502, 0x2502, 1}}) + screen:expect([[ + 1 │1 | + ^+-- 2 lines: 2----------│+-- 2 lines: 2---------| + 4 │4 | + ~ │~ | + | + ]]) + + -- change "vert" character to double-cell + funcs.setcellwidths({{0x2502, 0x2502, 2}}) + screen:expect([[ + 1 |1 | + ^+-- 2 lines: 2----------|+-- 2 lines: 2---------| + 4 |4 | + ~ |~ | + | + ]]) + + -- "vert" character should still default to single-byte fillchars because of setcellwidths(). + command('set ambiwidth=single') + screen:expect([[ + 1 |1 | + ^+-- 2 lines: 2··········|+-- 2 lines: 2·········| + 4 |4 | + ~ |~ | + | + ]]) end) end) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index cd7415de90..fa731f6faf 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -3181,4 +3181,32 @@ describe('LSP', function() } end) end) + + describe('cmd', function() + it('can connect to lsp server via rpc.connect', function() + local result = exec_lua [[ + local uv = vim.loop + local server = uv.new_tcp() + local init = nil + server:bind('127.0.0.1', 0) + server:listen(127, function(err) + assert(not err, err) + local socket = uv.new_tcp() + server:accept(socket) + socket:read_start(require('vim.lsp.rpc').create_read_loop(function(body) + init = body + socket:close() + end)) + end) + local port = server:getsockname().port + vim.lsp.start({ name = 'dummy', cmd = vim.lsp.rpc.connect('127.0.0.1', port) }) + vim.wait(1000, function() return init ~= nil end) + assert(init, "server must receive `initialize` request") + server:close() + server:shutdown() + return vim.json.decode(init) + ]] + eq(result.method, "initialize") + end) + end) end) diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua index c8da5a711f..9304aa6da9 100644 --- a/test/functional/plugin/man_spec.lua +++ b/test/functional/plugin/man_spec.lua @@ -6,6 +6,12 @@ local funcs = helpers.funcs local nvim_prog = helpers.nvim_prog local matches = helpers.matches +clear() +if funcs.executable('man') == 0 then + pending('missing "man" command', function() end) + return +end + describe(':Man', function() before_each(function() clear() diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua index 5bdfec574e..fbaef3ae00 100644 --- a/test/functional/provider/clipboard_spec.lua +++ b/test/functional/provider/clipboard_spec.lua @@ -96,9 +96,9 @@ describe('clipboard', function() [0] = {bold = true, foreground = Screen.colors.Blue}, [1] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, [2] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [3] = {bold = true, reverse = true}; }) screen:attach() - command("set display-=msgsep") end) it('unnamed register works without provider', function() @@ -123,10 +123,10 @@ describe('clipboard', function() command("let g:clipboard = 'bogus'") feed_command('redir @+> | bogus_cmd | redir END') screen:expect{grid=[[ - {0:~ }| - clipboard: No provider. Try ":checkhealth" or ":h clipboard". | - {1:E492: Not an editor command: bogus_cmd | redir END} | - {2:Press ENTER or type command to continue}^ | + {3: }| + clipboard: No provider. Try ":checkhealth" or ":h clipboard". | + {1:E492: Not an editor command: bogus_cmd | redir END} | + {2:Press ENTER or type command to continue}^ | ]]} end) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index eee759d2be..4f444316c3 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -297,6 +297,199 @@ describe('TUI', function() ]], attrs) end) + it('accepts mouse wheel events #19992', function() + child_session:request('nvim_command', [[ + set number nostartofline nowrap mousescroll=hor:1,ver:1 + call setline(1, repeat([join(range(10), '----')], 10)) + vsplit + ]]) + screen:expect([[ + {11: 1 }{1:0}----1----2----3----4│{11: 1 }0----1----2----3----| + {11: 2 }0----1----2----3----4│{11: 2 }0----1----2----3----| + {11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----| + {11: 4 }0----1----2----3----4│{11: 4 }0----1----2----3----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelDown> in active window + feed_data('\027[<65;8;1M') + screen:expect([[ + {11: 2 }{1:0}----1----2----3----4│{11: 1 }0----1----2----3----| + {11: 3 }0----1----2----3----4│{11: 2 }0----1----2----3----| + {11: 4 }0----1----2----3----4│{11: 3 }0----1----2----3----| + {11: 5 }0----1----2----3----4│{11: 4 }0----1----2----3----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelDown> in inactive window + feed_data('\027[<65;48;1M') + screen:expect([[ + {11: 2 }{1:0}----1----2----3----4│{11: 2 }0----1----2----3----| + {11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----| + {11: 4 }0----1----2----3----4│{11: 4 }0----1----2----3----| + {11: 5 }0----1----2----3----4│{11: 5 }0----1----2----3----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelRight> in active window + feed_data('\027[<67;8;1M') + screen:expect([[ + {11: 2 }{1:-}---1----2----3----4-│{11: 2 }0----1----2----3----| + {11: 3 }----1----2----3----4-│{11: 3 }0----1----2----3----| + {11: 4 }----1----2----3----4-│{11: 4 }0----1----2----3----| + {11: 5 }----1----2----3----4-│{11: 5 }0----1----2----3----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelRight> in inactive window + feed_data('\027[<67;48;1M') + screen:expect([[ + {11: 2 }{1:-}---1----2----3----4-│{11: 2 }----1----2----3----4| + {11: 3 }----1----2----3----4-│{11: 3 }----1----2----3----4| + {11: 4 }----1----2----3----4-│{11: 4 }----1----2----3----4| + {11: 5 }----1----2----3----4-│{11: 5 }----1----2----3----4| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelDown> in active window + feed_data('\027[<69;8;1M') + screen:expect([[ + {11: 5 }{1:-}---1----2----3----4-│{11: 2 }----1----2----3----4| + {11: 6 }----1----2----3----4-│{11: 3 }----1----2----3----4| + {11: 7 }----1----2----3----4-│{11: 4 }----1----2----3----4| + {11: 8 }----1----2----3----4-│{11: 5 }----1----2----3----4| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelDown> in inactive window + feed_data('\027[<69;48;1M') + screen:expect([[ + {11: 5 }{1:-}---1----2----3----4-│{11: 5 }----1----2----3----4| + {11: 6 }----1----2----3----4-│{11: 6 }----1----2----3----4| + {11: 7 }----1----2----3----4-│{11: 7 }----1----2----3----4| + {11: 8 }----1----2----3----4-│{11: 8 }----1----2----3----4| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelRight> in active window + feed_data('\027[<71;8;1M') + screen:expect([[ + {11: 5 }{1:-}---6----7----8----9 │{11: 5 }----1----2----3----4| + {11: 6 }----6----7----8----9 │{11: 6 }----1----2----3----4| + {11: 7 }----6----7----8----9 │{11: 7 }----1----2----3----4| + {11: 8 }----6----7----8----9 │{11: 8 }----1----2----3----4| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelRight> in inactive window + feed_data('\027[<71;48;1M') + screen:expect([[ + {11: 5 }{1:-}---6----7----8----9 │{11: 5 }5----6----7----8----| + {11: 6 }----6----7----8----9 │{11: 6 }5----6----7----8----| + {11: 7 }----6----7----8----9 │{11: 7 }5----6----7----8----| + {11: 8 }----6----7----8----9 │{11: 8 }5----6----7----8----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelUp> in active window + feed_data('\027[<64;8;1M') + screen:expect([[ + {11: 4 }----6----7----8----9 │{11: 5 }5----6----7----8----| + {11: 5 }{1:-}---6----7----8----9 │{11: 6 }5----6----7----8----| + {11: 6 }----6----7----8----9 │{11: 7 }5----6----7----8----| + {11: 7 }----6----7----8----9 │{11: 8 }5----6----7----8----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelUp> in inactive window + feed_data('\027[<64;48;1M') + screen:expect([[ + {11: 4 }----6----7----8----9 │{11: 4 }5----6----7----8----| + {11: 5 }{1:-}---6----7----8----9 │{11: 5 }5----6----7----8----| + {11: 6 }----6----7----8----9 │{11: 6 }5----6----7----8----| + {11: 7 }----6----7----8----9 │{11: 7 }5----6----7----8----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelLeft> in active window + feed_data('\027[<66;8;1M') + screen:expect([[ + {11: 4 }5----6----7----8----9│{11: 4 }5----6----7----8----| + {11: 5 }5{1:-}---6----7----8----9│{11: 5 }5----6----7----8----| + {11: 6 }5----6----7----8----9│{11: 6 }5----6----7----8----| + {11: 7 }5----6----7----8----9│{11: 7 }5----6----7----8----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelLeft> in inactive window + feed_data('\027[<66;48;1M') + screen:expect([[ + {11: 4 }5----6----7----8----9│{11: 4 }-5----6----7----8---| + {11: 5 }5{1:-}---6----7----8----9│{11: 5 }-5----6----7----8---| + {11: 6 }5----6----7----8----9│{11: 6 }-5----6----7----8---| + {11: 7 }5----6----7----8----9│{11: 7 }-5----6----7----8---| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelUp> in active window + feed_data('\027[<68;8;1M') + screen:expect([[ + {11: 1 }5----6----7----8----9│{11: 4 }-5----6----7----8---| + {11: 2 }5----6----7----8----9│{11: 5 }-5----6----7----8---| + {11: 3 }5----6----7----8----9│{11: 6 }-5----6----7----8---| + {11: 4 }5{1:-}---6----7----8----9│{11: 7 }-5----6----7----8---| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelUp> in inactive window + feed_data('\027[<68;48;1M') + screen:expect([[ + {11: 1 }5----6----7----8----9│{11: 1 }-5----6----7----8---| + {11: 2 }5----6----7----8----9│{11: 2 }-5----6----7----8---| + {11: 3 }5----6----7----8----9│{11: 3 }-5----6----7----8---| + {11: 4 }5{1:-}---6----7----8----9│{11: 4 }-5----6----7----8---| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelLeft> in active window + feed_data('\027[<70;8;1M') + screen:expect([[ + {11: 1 }0----1----2----3----4│{11: 1 }-5----6----7----8---| + {11: 2 }0----1----2----3----4│{11: 2 }-5----6----7----8---| + {11: 3 }0----1----2----3----4│{11: 3 }-5----6----7----8---| + {11: 4 }0----1----2----3----{1:4}│{11: 4 }-5----6----7----8---| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelLeft> in inactive window + feed_data('\027[<70;48;1M') + screen:expect([[ + {11: 1 }0----1----2----3----4│{11: 1 }0----1----2----3----| + {11: 2 }0----1----2----3----4│{11: 2 }0----1----2----3----| + {11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----| + {11: 4 }0----1----2----3----{1:4}│{11: 4 }0----1----2----3----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + end) + it('accepts keypad keys from kitty keyboard protocol #19180', function() feed_data('i') feed_data(funcs.nr2char(57399)) -- KP_0 @@ -1145,6 +1338,47 @@ describe('TUI', function() {3:-- TERMINAL --} | ]=]) end) + + it('allows grid to assume wider ambiguous-width characters than host terminal #19686', function() + child_session:request('nvim_buf_set_lines', 0, 0, 0, true, { ('℃'):rep(60), ('℃'):rep(60) }) + child_session:request('nvim_win_set_option', 0, 'cursorline', true) + child_session:request('nvim_win_set_option', 0, 'list', true) + child_session:request('nvim_win_set_option', 0, 'listchars', 'eol:$') + local attrs = screen:get_default_attr_ids() + attrs[11] = {underline = true} -- CursorLine + attrs[12] = {underline = true, reverse = true} -- CursorLine and TermCursor + attrs[13] = {underline = true, foreground = 12} -- CursorLine and NonText + feed_data('gg') + local singlewidth_screen = [[ + {12:℃}{11:℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃}| + {11:℃℃℃℃℃℃℃℃℃℃}{13:$}{11: }| + ℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃| + ℃℃℃℃℃℃℃℃℃℃{4:$} | + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]] + -- When grid assumes "℃" to be double-width but host terminal assumes it to be single-width, the + -- second cell of "℃" is a space and the attributes of the "℃" are applied to it. + local doublewidth_screen = [[ + {12:℃}{11: ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }| + {11:℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }| + {11:℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }{13:$}{11: }| + ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ >{4:@@@}| + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]] + screen:expect(singlewidth_screen, attrs) + child_session:request('nvim_set_option', 'ambiwidth', 'double') + screen:expect(doublewidth_screen, attrs) + child_session:request('nvim_set_option', 'ambiwidth', 'single') + screen:expect(singlewidth_screen, attrs) + child_session:request('nvim_call_function', 'setcellwidths', {{{0x2103, 0x2103, 2}}}) + screen:expect(doublewidth_screen, attrs) + child_session:request('nvim_call_function', 'setcellwidths', {{{0x2103, 0x2103, 1}}}) + screen:expect(singlewidth_screen, attrs) + end) end) describe('TUI', function() diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index 5ec0a8a060..4b0bd1eb50 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -6,11 +6,14 @@ local insert = helpers.insert local exec_lua = helpers.exec_lua local feed = helpers.feed local pending_c_parser = helpers.pending_c_parser +local command = helpers.command +local meths = helpers.meths +local eq = helpers.eq before_each(clear) local hl_query = [[ - (ERROR) @ErrorMsg + (ERROR) @error "if" @keyword "else" @keyword @@ -23,23 +26,24 @@ local hl_query = [[ "enum" @type "extern" @type - (string_literal) @string.nonexistent-specializer-for-string.should-fallback-to-string + ; nonexistent specializer for string should fallback to string + (string_literal) @string.nonexistent_specializer (number_literal) @number (char_literal) @string (type_identifier) @type - ((type_identifier) @Special (#eq? @Special "LuaRef")) + ((type_identifier) @constant.builtin (#eq? @constant.builtin "LuaRef")) (primitive_type) @type (sized_type_specifier) @type ; Use lua regexes - ((identifier) @Identifier (#contains? @Identifier "lua_")) + ((identifier) @function (#contains? @function "lua_")) ((identifier) @Constant (#lua-match? @Constant "^[A-Z_]+$")) - ((identifier) @Normal (#vim-match? @Constant "^lstate$")) + ((identifier) @Normal (#vim-match? @Normal "^lstate$")) - ((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right) (#eq? @WarningMsg.left @WarningMsg.right)) + ((binary_expression left: (identifier) @warning.left right: (identifier) @warning.right) (#eq? @warning.left @warning.right)) (comment) @comment ]] @@ -103,6 +107,7 @@ describe('treesitter highlighting', function() } exec_lua([[ hl_query = ... ]], hl_query) + command [[ hi link @warning WarningMsg ]] end) it('is updated with edits', function() @@ -547,7 +552,7 @@ describe('treesitter highlighting', function() -- This will change ONLY the literal strings to look like comments -- The only literal string is the "vim.schedule: expected function" in this test. - exec_lua [[vim.cmd("highlight link cString comment")]] + exec_lua [[vim.cmd("highlight link @string.nonexistent_specializer comment")]] screen:expect{grid=[[ {2:/// Schedule Lua callback on main loop's event queue} | {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | @@ -612,6 +617,11 @@ describe('treesitter highlighting', function() -- bold will not be overwritten at the moment [12] = {background = Screen.colors.Red, bold = true, foreground = Screen.colors.Grey100}; }} + + eq({ + {capture='Error', priority='101'}; + {capture='type'}; + }, exec_lua [[ return vim.treesitter.get_captures_at_position(0, 0, 2) ]]) end) it("allows to use captures with dots (don't use fallback when specialization of foo exists)", function() @@ -642,11 +652,13 @@ describe('treesitter highlighting', function() | ]]} + command [[ + hi link @foo.bar Type + hi link @foo String + ]] exec_lua [[ local parser = vim.treesitter.get_parser(0, "c", {}) local highlighter = vim.treesitter.highlighter - highlighter.hl_map['foo.bar'] = 'Type' - highlighter.hl_map['foo'] = 'String' test_hl = highlighter.new(parser, {queries = {c = "(primitive_type) @foo.bar (string_literal) @foo"}}) ]] @@ -670,6 +682,29 @@ describe('treesitter highlighting', function() {1:~ }| | ]]} + + -- clearing specialization reactivates fallback + command [[ hi clear @foo.bar ]] + screen:expect{grid=[[ + {5:char}* x = {5:"Will somebody ever read this?"}; | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} end) it("supports conceal attribute", function() @@ -712,32 +747,26 @@ describe('treesitter highlighting', function() ]]} end) - it("hl_map has the correct fallback behavior", function() - exec_lua [[ - local hl_map = vim.treesitter.highlighter.hl_map - hl_map["foo"] = 1 - hl_map["foo.bar"] = 2 - hl_map["foo.bar.baz"] = 3 - - assert(hl_map["foo"] == 1) - assert(hl_map["foo.a.b.c.d"] == 1) - assert(hl_map["foo.bar"] == 2) - assert(hl_map["foo.bar.a.b.c.d"] == 2) - assert(hl_map["foo.bar.baz"] == 3) - assert(hl_map["foo.bar.baz.d"] == 3) - - hl_map["FOO"] = 1 - hl_map["FOO.BAR"] = 2 - assert(hl_map["FOO.BAR.BAZ"] == 2) - - hl_map["foo.missing.exists"] = 3 - assert(hl_map["foo.missing"] == 1) - assert(hl_map["foo.missing.exists"] == 3) - assert(hl_map["foo.missing.exists.bar"] == 3) - assert(hl_map["total.nonsense.but.a.lot.of.dots"] == nil) - -- It will not perform a second look up of this variable but return a sentinel value - assert(hl_map["total.nonsense.but.a.lot.of.dots"] == "__notfound") - ]] - + it("@foo.bar groups has the correct fallback behavior", function() + local get_hl = function(name) return meths.get_hl_by_name(name,1).foreground end + meths.set_hl(0, "@foo", {fg = 1}) + meths.set_hl(0, "@foo.bar", {fg = 2}) + meths.set_hl(0, "@foo.bar.baz", {fg = 3}) + + eq(1, get_hl"@foo") + eq(1, get_hl"@foo.a.b.c.d") + eq(2, get_hl"@foo.bar") + eq(2, get_hl"@foo.bar.a.b.c.d") + eq(3, get_hl"@foo.bar.baz") + eq(3, get_hl"@foo.bar.baz.d") + + -- lookup is case insensitive + eq(2, get_hl"@FOO.BAR.SPAM") + + meths.set_hl(0, "@foo.missing.exists", {fg = 3}) + eq(1, get_hl"@foo.missing") + eq(3, get_hl"@foo.missing.exists") + eq(3, get_hl"@foo.missing.exists.bar") + eq(nil, get_hl"@total.nonsense.but.a.lot.of.dots") end) end) diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index 30585be328..8e9941d797 100644 --- a/test/functional/treesitter/language_spec.lua +++ b/test/functional/treesitter/language_spec.lua @@ -7,10 +7,11 @@ local exec_lua = helpers.exec_lua local pcall_err = helpers.pcall_err local matches = helpers.matches local pending_c_parser = helpers.pending_c_parser +local insert = helpers.insert before_each(clear) -describe('treesitter API', function() +describe('treesitter language API', function() -- error tests not requiring a parser library it('handles missing language', function() eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", @@ -26,6 +27,11 @@ describe('treesitter API', function() eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", pcall_err(exec_lua, "parser = vim.treesitter.inspect_language('borklang')")) + + if not pending_c_parser(pending) then + matches("Error executing lua: Failed to load parser: uv_dlsym: .+", + pcall_err(exec_lua, 'vim.treesitter.require_language("c", nil, false, "borklang")')) + end end) it('inspects language', function() @@ -79,5 +85,35 @@ describe('treesitter API', function() eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", pcall_err(exec_lua, "new_parser = vim.treesitter.get_parser(0)")) end) + + it('retrieve the tree given a range', function () + if pending_c_parser(pending) then return end + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua([[ + langtree = vim.treesitter.get_parser(0, "c") + tree = langtree:tree_for_range({1, 3, 1, 3}) + ]]) + + eq('<node translation_unit>', exec_lua('return tostring(tree:root())')) + end) + + it('retrieve the node given a range', function () + if pending_c_parser(pending) then return end + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua([[ + langtree = vim.treesitter.get_parser(0, "c") + node = langtree:named_node_for_range({1, 3, 1, 3}) + ]]) + + eq('<node primitive_type>', exec_lua('return tostring(node)')) + end) end) diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua index 21c287644e..87ce1b973c 100644 --- a/test/functional/treesitter/node_spec.lua +++ b/test/functional/treesitter/node_spec.lua @@ -59,4 +59,53 @@ describe('treesitter node API', function() exec_lua 'node = node:prev_named_sibling()' eq('int x', lua_eval('node_text(node)')) end) + + it('can retrieve the children of a node', function() + insert([[ + int main() { + int x = 3; + }]]) + + local len = exec_lua([[ + tree = vim.treesitter.get_parser(0, "c"):parse()[1] + node = tree:root():child(0) + children = node:named_children() + + return #children + ]]) + + eq(3, len) + eq('<node compound_statement>', lua_eval('tostring(children[3])')) + end) + + it('can retrieve the tree root given a node', function() + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua([[ + tree = vim.treesitter.get_parser(0, "c"):parse()[1] + root = tree:root() + node = root:child(0):child(2) + ]]) + + eq(lua_eval('tostring(root)'), lua_eval('tostring(node:root())')) + end) + + it('can compute the byte length of a node', function() + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua([[ + tree = vim.treesitter.get_parser(0, "c"):parse()[1] + root = tree:root() + child = root:child(0):child(0) + ]]) + + eq(28, lua_eval('root:byte_length()')) + eq(3, lua_eval('child:byte_length()')) + end) end) diff --git a/test/functional/treesitter/utils_spec.lua b/test/functional/treesitter/utils_spec.lua new file mode 100644 index 0000000000..4f4c18a748 --- /dev/null +++ b/test/functional/treesitter/utils_spec.lua @@ -0,0 +1,33 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local insert = helpers.insert +local eq = helpers.eq +local exec_lua = helpers.exec_lua +local pending_c_parser = helpers.pending_c_parser + +before_each(clear) + +describe('treesitter utils', function() + before_each(clear) + + it('can find an ancestor', function() + if pending_c_parser(pending) then return end + + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + root = tree:root() + ancestor = root:child(0) + child = ancestor:child(0) + ]]) + + eq(true, exec_lua('return vim.treesitter.is_ancestor(ancestor, child)')) + eq(false, exec_lua('return vim.treesitter.is_ancestor(child, ancestor)')) + end) +end) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 384761ab17..fa5771a8b3 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -24,7 +24,6 @@ before_each(function() clear() screen = Screen.new(40, 8) screen:attach() - command("set display-=msgsep") source([[ highlight RBP1 guibg=Red highlight RBP2 guibg=Yellow @@ -152,6 +151,7 @@ before_each(function() SB={foreground = Screen.colors.Blue4}, E={foreground = Screen.colors.Red, background = Screen.colors.Blue}, M={bold = true}, + MSEP={bold = true, reverse = true}; }) end) @@ -298,22 +298,22 @@ describe('Command-line coloring', function() function() set_color_cb('SplittedMultibyteStart') start_prompt('echo "«') - screen:expect([[ - {EOB:~ }| - {EOB:~ }| + screen:expect{grid=[[ + | {EOB:~ }| {EOB:~ }| + {MSEP: }| :echo " | {ERR:E5405: Chunk 0 start 7 splits multibyte }| {ERR:character} | :echo "«^ | - ]]) + ]]} feed('»') screen:expect([[ + | {EOB:~ }| {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| :echo " | {ERR:E5405: Chunk 0 start 7 splits multibyte }| {ERR:character} | @@ -325,10 +325,10 @@ describe('Command-line coloring', function() set_color_cb('SplittedMultibyteEnd') start_prompt('echo "«') screen:expect([[ + | {EOB:~ }| {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| :echo " | {ERR:E5406: Chunk 0 end 7 splits multibyte ch}| {ERR:aracter} | @@ -339,10 +339,10 @@ describe('Command-line coloring', function() set_color_cb('Echoerring') start_prompt('e') screen:expect([[ + | {EOB:~ }| {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| : | {ERR:E5407: Callback has thrown an exception:}| {ERR: Vim(echoerr):HERE} | @@ -398,10 +398,10 @@ describe('Command-line coloring', function() set_color_cb('Throwing') start_prompt('e') screen:expect([[ + | {EOB:~ }| {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| : | {ERR:E5407: Callback has thrown an exception:}| {ERR: ABC} | @@ -412,10 +412,10 @@ describe('Command-line coloring', function() set_color_cb('SplittedMultibyteStart') start_prompt('let x = "«»«»«»«»«»"') screen:expect([[ + | {EOB:~ }| {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| :let x = " | {ERR:E5405: Chunk 0 start 10 splits multibyte}| {ERR: character} | @@ -453,10 +453,10 @@ describe('Command-line coloring', function() screen:sleep(500) feed('<C-c>') screen:expect([[ + | {EOB:~ }| {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| : | {ERR:E5407: Callback has thrown an exception:}| {ERR: Keyboard interrupt} | @@ -517,11 +517,11 @@ describe('Command-line coloring', function() set_color_cb('ReturningGlobal', '') start_prompt('#') screen:expect([[ + | {EOB:~ }| {EOB:~ }| {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| : | {ERR:E5400: Callback should return list} | :#^ | @@ -531,11 +531,11 @@ describe('Command-line coloring', function() set_color_cb('ReturningGlobal', {{0, 1, 'Normal'}, 42}) start_prompt('#') screen:expect([[ + | {EOB:~ }| {EOB:~ }| {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| : | {ERR:E5401: List item 1 is not a List} | :#^ | @@ -545,10 +545,10 @@ describe('Command-line coloring', function() set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {1}}) start_prompt('+') screen:expect([[ + | {EOB:~ }| {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| :+ | {ERR:E5402: List item 1 has incorrect length:}| {ERR: 1 /= 3} | @@ -559,10 +559,10 @@ describe('Command-line coloring', function() set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {2, 3, 'Normal'}}) start_prompt('+') screen:expect([[ + | {EOB:~ }| {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| :+ | {ERR:E5403: Chunk 1 start 2 not in range [1, }| {ERR:2)} | @@ -573,10 +573,10 @@ describe('Command-line coloring', function() set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {1, 3, 'Normal'}}) start_prompt('+') screen:expect([[ + | {EOB:~ }| {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| :+ | {ERR:E5404: Chunk 1 end 3 not in range (1, 2]}| | @@ -800,10 +800,10 @@ describe('Ex commands coloring', function() it('does not crash when using `n` in debug mode', function() feed(':debug execute "echo 1"\n') screen:expect([[ + | {EOB:~ }| {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| Entering Debug mode. Type "cont" to con| tinue. | cmd: execute "echo 1" | @@ -811,8 +811,8 @@ describe('Ex commands coloring', function() ]]) feed('n\n') screen:expect([[ - {EOB:~ }| - {EOB:~ }| + | + {MSEP: }| Entering Debug mode. Type "cont" to con| tinue. | cmd: execute "echo 1" | @@ -836,10 +836,10 @@ describe('Ex commands coloring', function() command("cnoremap <expr> x execute('throw 42')[-1]") feed(':#x') screen:expect([[ + | {EOB:~ }| {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| :# | {ERR:Error detected while processing :} | {ERR:E605: Exception not caught: 42} | @@ -847,9 +847,9 @@ describe('Ex commands coloring', function() ]]) feed('<CR>') screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| :# | {ERR:Error detected while processing :} | {ERR:E605: Exception not caught: 42} | @@ -864,9 +864,9 @@ describe('Ex commands coloring', function() meths.set_var('Nvim_color_cmdline', 42) feed(':#') screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {MSEP: }| : | {ERR:E5408: Unable to get g:Nvim_color_cmdlin}| {ERR:e callback: Vim:E6000: Argument is not a}| diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 9af5d386db..789f1c6487 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -193,7 +193,7 @@ describe('decorations providers', function() | ]]} - meths._set_hl_ns(ns1) + meths.set_hl_ns(ns1) screen:expect{grid=[[ {10: 1 }{11:// just to see if there was an accid}| {10: }{11:ent} | @@ -219,7 +219,7 @@ describe('decorations providers', function() local ns2 = a.nvim_create_namespace 'ns2' a.nvim_set_decoration_provider (ns2, { on_win = function (_, win, buf) - a.nvim__set_hl_ns(win == thewin and _G.ns1 or ns2) + a.nvim_set_hl_ns_fast(win == thewin and _G.ns1 or ns2) end; }) ]] @@ -266,7 +266,7 @@ describe('decorations providers', function() ]]} meths.set_hl(ns1, 'LinkGroup', {fg = 'Blue'}) - meths._set_hl_ns(ns1) + meths.set_hl_ns(ns1) screen:expect{grid=[[ // just to see if there was an accident | @@ -302,7 +302,7 @@ describe('decorations providers', function() ]]} meths.set_hl(ns1, 'LinkGroup', {fg = 'Blue', default=true}) - meths._set_hl_ns(ns1) + meths.set_hl_ns(ns1) feed 'k' screen:expect{grid=[[ @@ -1681,7 +1681,7 @@ l5 screen:expect{grid=[[ S4S1^l1 | - S2x l2 | + x S2l2 | S5{1: }l3 | {1: }l4 | {1: }l5 | @@ -1779,6 +1779,34 @@ l5 ]]} end) + it('works with priority #19716', function() + screen:try_resize(20, 3) + insert(example_text) + feed 'gg' + + helpers.command('sign define Oldsign text=O3') + helpers.command([[exe 'sign place 42 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]]) + + meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S4', priority=100}) + meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S2', priority=5}) + meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S5', priority=200}) + meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1', priority=1}) + + screen:expect{grid=[[ + S1S2O3S4S5^l1 | + {1: }l2 | + | + ]]} + + -- Check truncation works too + meths.win_set_option(0, 'signcolumn', 'auto') + + screen:expect{grid=[[ + S5^l1 | + {1: }l2 | + | + ]]} + end) end) describe('decorations: virt_text', function() diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index c79fc2989c..6bb8bb81c6 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -1904,4 +1904,11 @@ describe("folded lines", function() describe('without ext_multigrid', function() with_ext_multigrid(false) end) + + it('no folds remains if :delete makes buffer empty #19671', function() + funcs.setline(1, {'foo', 'bar', 'baz'}) + command('2,3fold') + command('%delete') + eq(0, funcs.foldlevel(1)) + end) end) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index e065a727f3..5ffe9ddaad 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -3,9 +3,10 @@ local Screen = require('test.functional.ui.screen') local os = require('os') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local command, exec = helpers.command, helpers.exec -local eval, exc_exec = helpers.eval, helpers.exc_exec +local eval = helpers.eval local feed_command, eq = helpers.feed_command, helpers.eq local curbufmeths = helpers.curbufmeths +local meths = helpers.meths describe('colorscheme compatibility', function() before_each(function() @@ -92,16 +93,22 @@ describe('highlight defaults', function() before_each(function() clear() screen = Screen.new() + screen:set_default_attr_ids { + [0] = {bold=true, foreground=Screen.colors.Blue}; + [1] = {reverse = true, bold = true}; + [2] = {reverse = true}; + [3] = {bold = true}; + [4] = {bold = true, foreground = Screen.colors.SeaGreen}; + [5] = {foreground = Screen.colors.Red1, background = Screen.colors.WebGreen}; + [6] = {background = Screen.colors.Red1, foreground = Screen.colors.Grey100}; + [7] = {foreground = Screen.colors.Red}; + [8] = {foreground = Screen.colors.Blue}; + [9] = {italic = true}; + } screen:attach() - command("set display-=msgsep") end) it('window status bar', function() - screen:set_default_attr_ids({ - [0] = {bold=true, foreground=Screen.colors.Blue}, - [1] = {reverse = true, bold = true}, -- StatusLine - [2] = {reverse = true} -- StatusLineNC - }) feed_command('sp', 'vsp', 'vsp') screen:expect([[ ^ │ │ | @@ -200,31 +207,29 @@ describe('highlight defaults', function() ^ | {0:~ }| {0:~ }| - {1:-- INSERT --} | - ]], {[0] = {bold=true, foreground=Screen.colors.Blue}, - [1] = {bold = true}}) + {3:-- INSERT --} | + ]]) end) it('end of file markers', function() screen:try_resize(53, 4) screen:expect([[ ^ | - {1:~ }| - {1:~ }| + {0:~ }| + {0:~ }| | - ]], {[1] = {bold = true, foreground = Screen.colors.Blue}}) + ]]) end) it('"wait return" text', function() screen:try_resize(53, 4) feed(':ls<cr>') screen:expect([[ - {0:~ }| + {1: }| :ls | 1 %a "[No Name]" line 1 | - {1:Press ENTER or type command to continue}^ | - ]], {[0] = {bold=true, foreground=Screen.colors.Blue}, - [1] = {bold = true, foreground = Screen.colors.SeaGreen}}) + {4:Press ENTER or type command to continue}^ | + ]]) feed('<cr>') -- skip the "Press ENTER..." state or tests will hang end) @@ -237,8 +242,7 @@ describe('highlight defaults', function() {0:~ }| {0:~ }| -- INSERT -- | - ]], {[0] = {bold=true, foreground=Screen.colors.Blue}, - [1] = {bold=true}}) + ]]) feed('<esc>') feed_command('highlight CustomHLGroup guifg=red guibg=green') feed_command('highlight link ModeMsg CustomHLGroup') @@ -247,9 +251,8 @@ describe('highlight defaults', function() ^ | {0:~ }| {0:~ }| - {1:-- INSERT --} | - ]], {[0] = {bold=true, foreground=Screen.colors.Blue}, - [1] = {foreground = Screen.colors.Red, background = Screen.colors.Green}}) + {5:-- INSERT --} | + ]]) end) it('can be cleared by assigning NONE', function() @@ -258,14 +261,11 @@ describe('highlight defaults', function() feed_command('hi link TmpKeyword ErrorMsg') insert('neovim') screen:expect([[ - {1:neovi^m} | + {6:neovi^m} | {0:~ }| {0:~ }| | - ]], { - [0] = {bold=true, foreground=Screen.colors.Blue}, - [1] = {foreground = Screen.colors.White, background = Screen.colors.Red} - }) + ]]) feed_command("hi ErrorMsg term=NONE cterm=NONE ctermfg=NONE ctermbg=NONE" .. " gui=NONE guifg=NONE guibg=NONE guisp=NONE") screen:expect([[ @@ -273,7 +273,7 @@ describe('highlight defaults', function() {0:~ }| {0:~ }| | - ]], {[0] = {bold=true, foreground=Screen.colors.Blue}}) + ]]) end) it('linking updates window highlight immediately #16552', function() @@ -283,7 +283,7 @@ describe('highlight defaults', function() {0:~ }| {0:~ }| | - ]], {[0] = {bold=true, foreground=Screen.colors.Blue}}) + ]]) feed_command("hi NonTextAlt guifg=Red") feed_command("hi! link NonText NonTextAlt") screen:expect([[ @@ -305,56 +305,44 @@ describe('highlight defaults', function() feed_command('set listchars=space:.,tab:>-,trail:*,eol:¬ list') insert(' ne \t o\tv im ') screen:expect([[ - ne{0:.>----.}o{0:>-----}v{0:..}im{0:*^*¬} | - {0:~ }| - {0:~ }| + ne{7:.>----.}o{7:>-----}v{7:..}im{7:*^*¬} | + {7:~ }| + {7:~ }| | - ]], { - [0] = {foreground=Screen.colors.Red}, - [1] = {foreground=Screen.colors.Blue}, - }) + ]]) feed_command('highlight Whitespace gui=NONE guifg=#0000FF') screen:expect([[ - ne{1:.>----.}o{1:>-----}v{1:..}im{1:*^*}{0:¬} | - {0:~ }| - {0:~ }| + ne{8:.>----.}o{8:>-----}v{8:..}im{8:*^*}{7:¬} | + {7:~ }| + {7:~ }| :highlight Whitespace gui=NONE guifg=#0000FF | - ]], { - [0] = {foreground=Screen.colors.Red}, - [1] = {foreground=Screen.colors.Blue}, - }) + ]]) end) it('are sent to UIs', function() screen:try_resize(53, 4) - screen:set_default_attr_ids({ - [0] = {}, - [1] = {bold = true, foreground = Screen.colors.Blue1}, - [2] = {bold = true, reverse = true}, - [3] = {italic=true} - }) screen:expect{grid=[[ ^ | - {1:~ }| - {1:~ }| + {0:~ }| + {0:~ }| | - ]], hl_groups={EndOfBuffer=1, MsgSeparator=2}} + ]], hl_groups={EndOfBuffer=0, MsgSeparator=1}} command('highlight EndOfBuffer gui=italic') screen:expect{grid=[[ ^ | - {3:~ }| - {3:~ }| + {9:~ }| + {9:~ }| | - ]], hl_groups={EndOfBuffer=3, MsgSeparator=2}} + ]], hl_groups={EndOfBuffer=9, MsgSeparator=1}} command('highlight clear EndOfBuffer') screen:expect{grid=[[ ^ | - {1:~ }| - {1:~ }| + {0:~ }| + {0:~ }| | - ]], hl_groups={EndOfBuffer=1, MsgSeparator=2}} + ]], hl_groups={EndOfBuffer=0, MsgSeparator=1}} end) end) @@ -1787,6 +1775,7 @@ describe("'winhighlight' highlight", function() [26] = {background = Screen.colors.Red}, [27] = {background = Screen.colors.DarkBlue, bold = true, foreground = Screen.colors.Green1}, [28] = {bold = true, foreground = Screen.colors.Brown}, + [29] = {foreground = Screen.colors.Blue1, background = Screen.colors.Red, bold = true}; }) command("hi Background1 guibg=DarkBlue") command("hi Background2 guibg=DarkGreen") @@ -1820,7 +1809,7 @@ describe("'winhighlight' highlight", function() ]]) end) - it('handles invalid values', function() + it('handles undefined groups', function() command("set winhl=Normal:Background1") screen:expect([[ {1:^ }| @@ -1833,19 +1822,44 @@ describe("'winhighlight' highlight", function() | ]]) - eq('Vim(set):E474: Invalid argument: winhl=xxx:yyy', - exc_exec("set winhl=xxx:yyy")) - eq('Normal:Background1', eval('&winhl')) + command("set winhl=xxx:yyy") + eq('xxx:yyy', eval('&winhl')) screen:expect{grid=[[ - {1:^ }| - {2:~ }| - {2:~ }| - {2:~ }| - {2:~ }| - {2:~ }| - {2:~ }| + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + end) + + it('can be changed to define different groups', function() + command("set winhl=EndOfBuffer:Background1") + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + command("set winhl=Normal:ErrorMsg") + screen:expect{grid=[[ + {15:^ }| + {29:~ }| + {29:~ }| + {29:~ }| + {29:~ }| + {29:~ }| + {29:~ }| | - ]], unchanged=true} + ]]} end) it('works local to the window', function() @@ -2270,4 +2284,236 @@ describe("'winhighlight' highlight", function() | ]]) end) + + + it("can override syntax groups", 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:~ }| + | + ]]) + + command('split') + command('set winhl=Foobar:Background1,Article:ErrorMsg') + screen:expect{grid=[[ + {15:the} {1:foobar} was {1:fooba}| + {1:^r} | + {0:~ }| + {3:[No Name] [+] }| + {25:the} {26:foobar} was {26:fooba}| + {26:r} | + {4:[No Name] [+] }| + | + ]]} + end) + + it('can be disabled in newly opened window #19823', function() + command('split | set winhl=Normal:ErrorMsg | set winhl=') + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + {3:[No Name] }| + | + {0:~ }| + {4:[No Name] }| + | + ]]} + + helpers.assert_alive() + end) + + it('can redraw statusline on cursor movement', function() + screen:try_resize(40, 8) + exec [[ + set statusline=%f%=%#Background1#%l,%c%V\ %P + split + ]] + insert [[ + some text + more text]] + screen:expect{grid=[[ + some text | + more tex^t | + {0:~ }| + {3:[No Name] }{1:2,9 All}| + some text | + more text | + {4:[No Name] }{1:1,1 All}| + | + ]]} + + command 'set winhl=Background1:Background2' + screen:expect{grid=[[ + some text | + more tex^t | + {0:~ }| + {3:[No Name] }{5:2,9 All}| + some text | + more text | + {4:[No Name] }{1:1,1 All}| + | + ]]} + + feed 'k' + screen:expect{grid=[[ + some tex^t | + more text | + {0:~ }| + {3:[No Name] }{5:1,9 All}| + some text | + more text | + {4:[No Name] }{1:1,1 All}| + | + ]]} + end) +end) + +describe('highlight namespaces', function() + local screen + local ns1, ns2 + + before_each(function() + clear() + screen = Screen.new(25,10) + screen:attach() + screen:set_default_attr_ids { + [1] = {foreground = Screen.colors.Blue, bold = true}; + [2] = {background = Screen.colors.DarkGrey}; + [3] = {italic = true, foreground = Screen.colors.DarkCyan, background = Screen.colors.DarkOrange4}; + [4] = {background = Screen.colors.Magenta4}; + [5] = {background = Screen.colors.Magenta4, foreground = Screen.colors.Crimson}; + [6] = {bold = true, reverse = true}; + [7] = {reverse = true}; + [8] = {foreground = Screen.colors.Gray20}; + } + + ns1 = meths.create_namespace 'grungy' + ns2 = meths.create_namespace 'ultrared' + + meths.set_hl(ns1, 'Normal', {bg='DarkGrey'}) + meths.set_hl(ns1, 'NonText', {bg='DarkOrange4', fg='DarkCyan', italic=true}) + meths.set_hl(ns2, 'Normal', {bg='DarkMagenta'}) + meths.set_hl(ns2, 'NonText', {fg='Crimson'}) + end) + + it('can be used globally', function() + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + meths.set_hl_ns(ns1) + screen:expect{grid=[[ + {2:^ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + | + ]]} + + meths.set_hl_ns(ns2) + screen:expect{grid=[[ + {4:^ }| + {5:~ }| + {5:~ }| + {5:~ }| + {5:~ }| + {5:~ }| + {5:~ }| + {5:~ }| + {5:~ }| + | + ]]} + + meths.set_hl_ns(0) + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('can be used per window', function() + local win1 = meths.get_current_win() + command 'split' + local win2 = meths.get_current_win() + command 'split' + + meths.win_set_hl_ns(win1, ns1) + meths.win_set_hl_ns(win2, ns2) + + screen:expect{grid=[[ + ^ | + {1:~ }| + {6:[No Name] }| + {4: }| + {5:~ }| + {7:[No Name] }| + {2: }| + {3:~ }| + {7:[No Name] }| + | + ]]} + end) + + it('redraws correctly when ns=0', function() + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + meths.set_hl(0, 'EndOfBuffer', {fg='#333333'}) + screen:expect{grid=[[ + ^ | + {8:~ }| + {8:~ }| + {8:~ }| + {8:~ }| + {8:~ }| + {8:~ }| + {8:~ }| + {8:~ }| + | + ]]} + end) end) diff --git a/test/functional/ui/hlstate_spec.lua b/test/functional/ui/hlstate_spec.lua index df7f34aa7f..dc74d6d401 100644 --- a/test/functional/ui/hlstate_spec.lua +++ b/test/functional/ui/hlstate_spec.lua @@ -119,13 +119,15 @@ describe('ext_hlstate detailed highlights', function() [3] = {{bold = true, reverse = true}, {{hi_name = "StatusLine", ui_name = "StatusLine", kind = "ui"}}}, [4] = {{reverse = true}, {{hi_name = "StatusLineNC", ui_name = "StatusLineNC", kind = "ui"}}}, [5] = {{background = Screen.colors.Red, foreground = Screen.colors.Grey100}, {{hi_name = "ErrorMsg", ui_name = "LineNr", kind = "ui"}}}, - [6] = {{bold = true, reverse = true}, {{hi_name = "MsgSeparator", ui_name = "Normal", kind = "ui"}}}, + [6] = {{bold = true, reverse = true}, {{hi_name = "Normal", ui_name = "Normal", kind = "ui"}}}, [7] = {{foreground = Screen.colors.Brown, bold = true, reverse = true}, {6, 1}}, - [8] = {{foreground = Screen.colors.Blue1, bold = true, reverse = true}, {6, 2}}, - [9] = {{bold = true, foreground = Screen.colors.Brown}, {{hi_name = "Statement", ui_name = "NormalNC", kind = "ui"}}}, + [8] = {{foreground = Screen.colors.Blue1, bold = true, reverse = true}, {6, 14}}, + [9] = {{bold = true, foreground = Screen.colors.Brown}, {{hi_name = "NormalNC", ui_name = "NormalNC", kind = "ui"}}}, [10] = {{bold = true, foreground = Screen.colors.Brown}, {9, 1}}, - [11] = {{bold = true, foreground = Screen.colors.Blue1}, {9, 2}}, + [11] = {{bold = true, foreground = Screen.colors.Blue1}, {9, 14}}, [12] = {{}, {{hi_name = "MsgArea", ui_name = "MsgArea", kind = "ui"}}}, + [13] = {{background = Screen.colors.Red1, foreground = Screen.colors.Gray100}, {{ui_name = "LineNr", kind = "ui", hi_name = "LineNr"}}}; + [14] = {{bold = true, foreground = Screen.colors.Blue}, {{ui_name = "EndOfBuffer", kind = "ui", hi_name = "EndOfBuffer"}}}; }) command("set number") @@ -143,16 +145,16 @@ describe('ext_hlstate detailed highlights', function() ]]) command("set winhl=LineNr:ErrorMsg") - screen:expect([[ - {5: 1 }^ | - {2:~ }| - {2:~ }| + screen:expect{grid=[[ + {13: 1 }^ | + {14:~ }| + {14:~ }| {3:[No Name] }| {1: 1 } | {2:~ }| {4:[No Name] }| {12: }| - ]]) + ]]} command("set winhl=Normal:MsgSeparator,NormalNC:Statement") screen:expect([[ diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index d8dd546a8d..9ca4673efe 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -66,7 +66,6 @@ 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}, @@ -142,11 +141,11 @@ describe(":substitute, 'inccommand' preserves", function() feed_command("ls") screen:expect([[ + BAC | {15:~ }| {15:~ }| {15:~ }| - {15:~ }| - {15:~ }| + {11: }| :ls | 1 %a + "[No Name]" | line 1 | @@ -1469,14 +1468,14 @@ describe("inccommand=nosplit", function() -- non-modifier prefix feed(':silent tabedit %s/tw/to') screen:expect([[ + Inc substitution on | two lines | Inc substitution on | two lines | | {15:~ }| {15:~ }| - {15:~ }| - {15:~ }| + {11: }| :silent tabedit %s/t| w/to^ | ]]) @@ -2656,6 +2655,7 @@ describe(":substitute", function() feed("\\rѫ ab \\rXXXX") screen:expect([[ + 7 8 9 | K L M | {12:JLKR £} | {12:ѫ ab } | @@ -2667,8 +2667,7 @@ describe(":substitute", function() {12:ѫ ab } | {11:[No Name] [+] }| | 7| {12:JLKR £} | - | 8|{12: ѫ ab } | - {10:[Preview] }| + {11: }| :%s/[a-z]/JLKR £\rѫ ab \rXXX| X^ | ]]) @@ -3001,8 +3000,8 @@ it('long :%s/ with inccommand does not collapse cmdline', function() feed(':%s/AAAAAAA', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A') screen:expect([[ - {15:~ }| - {15:~ }| + | + {11: }| :%s/AAAAAAAA| AAAAAAAAAAAA| AAAAAAA^ | diff --git a/test/functional/ui/inccommand_user_spec.lua b/test/functional/ui/inccommand_user_spec.lua index b5816f6fe6..0b25d4f8d2 100644 --- a/test/functional/ui/inccommand_user_spec.lua +++ b/test/functional/ui/inccommand_user_spec.lua @@ -7,61 +7,82 @@ local feed = helpers.feed local command = helpers.command local assert_alive = helpers.assert_alive --- Implements a :Replace command that works like :substitute. +-- Implements a :Replace command that works like :substitute and has multibuffer support. local setup_replace_cmd = [[ - local function show_replace_preview(buf, use_preview_win, preview_ns, preview_buf, matches) + local function show_replace_preview(use_preview_win, preview_ns, preview_buf, matches) -- Find the width taken by the largest line number, used for padding the line numbers local highest_lnum = math.max(matches[#matches][1], 1) local highest_lnum_width = math.floor(math.log10(highest_lnum)) local preview_buf_line = 0 - - vim.g.prevns = preview_ns - vim.g.prevbuf = preview_buf + local multibuffer = #matches > 1 for _, match in ipairs(matches) do - local lnum = match[1] - local line_matches = match[2] - local prefix - - if use_preview_win then - prefix = string.format( - '|%s%d| ', - string.rep(' ', highest_lnum_width - math.floor(math.log10(lnum))), - lnum - ) + local buf = match[1] + local buf_matches = match[2] + + if multibuffer and #buf_matches > 0 and use_preview_win then + local bufname = vim.api.nvim_buf_get_name(buf) + + if bufname == "" then + bufname = string.format("Buffer #%d", buf) + end vim.api.nvim_buf_set_lines( preview_buf, preview_buf_line, preview_buf_line, 0, - { prefix .. vim.api.nvim_buf_get_lines(buf, lnum - 1, lnum, false)[1] } + { bufname .. ':' } ) + + preview_buf_line = preview_buf_line + 1 end - for _, line_match in ipairs(line_matches) do - vim.api.nvim_buf_add_highlight( - buf, - preview_ns, - 'Substitute', - lnum - 1, - line_match[1], - line_match[2] - ) + for _, buf_match in ipairs(buf_matches) do + local lnum = buf_match[1] + local line_matches = buf_match[2] + local prefix if use_preview_win then - vim.api.nvim_buf_add_highlight( + prefix = string.format( + '|%s%d| ', + string.rep(' ', highest_lnum_width - math.floor(math.log10(lnum))), + lnum + ) + + vim.api.nvim_buf_set_lines( preview_buf, + preview_buf_line, + preview_buf_line, + 0, + { prefix .. vim.api.nvim_buf_get_lines(buf, lnum - 1, lnum, false)[1] } + ) + end + + for _, line_match in ipairs(line_matches) do + vim.api.nvim_buf_add_highlight( + buf, preview_ns, 'Substitute', - preview_buf_line, - #prefix + line_match[1], - #prefix + line_match[2] + lnum - 1, + line_match[1], + line_match[2] ) + + if use_preview_win then + vim.api.nvim_buf_add_highlight( + preview_buf, + preview_ns, + 'Substitute', + preview_buf_line, + #prefix + line_match[1], + #prefix + line_match[2] + ) + end end - end - preview_buf_line = preview_buf_line + 1 + preview_buf_line = preview_buf_line + 1 + end end if use_preview_win then @@ -72,94 +93,121 @@ local setup_replace_cmd = [[ end local function do_replace(opts, preview, preview_ns, preview_buf) - local pat1 = opts.fargs[1] or '' + local pat1 = opts.fargs[1] + + if not pat1 then return end + local pat2 = opts.fargs[2] or '' local line1 = opts.line1 local line2 = opts.line2 - - local buf = vim.api.nvim_get_current_buf() - local lines = vim.api.nvim_buf_get_lines(buf, line1 - 1, line2, 0) local matches = {} - for i, line in ipairs(lines) do - local startidx, endidx = 0, 0 - local line_matches = {} - local num = 1 + -- Get list of valid and listed buffers + local buffers = vim.tbl_filter( + function(buf) + if not (vim.api.nvim_buf_is_valid(buf) and vim.bo[buf].buflisted and buf ~= preview_buf) + then + return false + end - while startidx ~= -1 do - local match = vim.fn.matchstrpos(line, pat1, 0, num) - startidx, endidx = match[2], match[3] + -- Check if there's at least one window using the buffer + for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do + if vim.api.nvim_win_get_buf(win) == buf then + return true + end + end - if startidx ~= -1 then - line_matches[#line_matches+1] = { startidx, endidx } - end + return false + end, + vim.api.nvim_list_bufs() + ) - num = num + 1 - end + for _, buf in ipairs(buffers) do + local lines = vim.api.nvim_buf_get_lines(buf, line1 - 1, line2, false) + local buf_matches = {} - if #line_matches > 0 then - matches[#matches+1] = { line1 + i - 1, line_matches } - end - end + for i, line in ipairs(lines) do + local startidx, endidx = 0, 0 + local line_matches = {} + local num = 1 - local new_lines = {} + while startidx ~= -1 do + local match = vim.fn.matchstrpos(line, pat1, 0, num) + startidx, endidx = match[2], match[3] - for _, match in ipairs(matches) do - local lnum = match[1] - local line_matches = match[2] - local line = lines[lnum - line1 + 1] - local pat_width_differences = {} - - -- If previewing, only replace the text in current buffer if pat2 isn't empty - -- Otherwise, always replace the text - if pat2 ~= '' or not preview then - if preview then - for _, line_match in ipairs(line_matches) do - local startidx, endidx = unpack(line_match) - local pat_match = line:sub(startidx + 1, endidx) - - pat_width_differences[#pat_width_differences+1] = - #vim.fn.substitute(pat_match, pat1, pat2, 'g') - #pat_match + if startidx ~= -1 then + line_matches[#line_matches+1] = { startidx, endidx } end + + num = num + 1 end - new_lines[lnum] = vim.fn.substitute(line, pat1, pat2, 'g') + if #line_matches > 0 then + buf_matches[#buf_matches+1] = { line1 + i - 1, line_matches } + end end - -- Highlight the matches if previewing - if preview then - local idx_offset = 0 - for i, line_match in ipairs(line_matches) do - local startidx, endidx = unpack(line_match) - -- Starting index of replacement text - local repl_startidx = startidx + idx_offset - -- Ending index of the replacement text (if pat2 isn't empty) - local repl_endidx - - if pat2 ~= '' then - repl_endidx = endidx + idx_offset + pat_width_differences[i] - else - repl_endidx = endidx + idx_offset + local new_lines = {} + + for _, buf_match in ipairs(buf_matches) do + local lnum = buf_match[1] + local line_matches = buf_match[2] + local line = lines[lnum - line1 + 1] + local pat_width_differences = {} + + -- If previewing, only replace the text in current buffer if pat2 isn't empty + -- Otherwise, always replace the text + if pat2 ~= '' or not preview then + if preview then + for _, line_match in ipairs(line_matches) do + local startidx, endidx = unpack(line_match) + local pat_match = line:sub(startidx + 1, endidx) + + pat_width_differences[#pat_width_differences+1] = + #vim.fn.substitute(pat_match, pat1, pat2, 'g') - #pat_match + end end - if pat2 ~= '' then - idx_offset = idx_offset + pat_width_differences[i] - end + new_lines[lnum] = vim.fn.substitute(line, pat1, pat2, 'g') + end - line_matches[i] = { repl_startidx, repl_endidx } + -- Highlight the matches if previewing + if preview then + local idx_offset = 0 + for i, line_match in ipairs(line_matches) do + local startidx, endidx = unpack(line_match) + -- Starting index of replacement text + local repl_startidx = startidx + idx_offset + -- Ending index of the replacement text (if pat2 isn't empty) + local repl_endidx + + if pat2 ~= '' then + repl_endidx = endidx + idx_offset + pat_width_differences[i] + else + repl_endidx = endidx + idx_offset + end + + if pat2 ~= '' then + idx_offset = idx_offset + pat_width_differences[i] + end + + line_matches[i] = { repl_startidx, repl_endidx } + end end end - end - for lnum, line in pairs(new_lines) do - vim.api.nvim_buf_set_lines(buf, lnum - 1, lnum, false, { line }) + for lnum, line in pairs(new_lines) do + vim.api.nvim_buf_set_lines(buf, lnum - 1, lnum, false, { line }) + end + + matches[#matches+1] = { buf, buf_matches } end if preview then local lnum = vim.api.nvim_win_get_cursor(0)[1] -- Use preview window only if preview buffer is provided and range isn't just the current line local use_preview_win = (preview_buf ~= nil) and (line1 ~= lnum or line2 ~= lnum) - return show_replace_preview(buf, use_preview_win, preview_ns, preview_buf, matches) + return show_replace_preview(use_preview_win, preview_ns, preview_buf, matches) end end @@ -354,3 +402,120 @@ describe("'inccommand' for user commands", function() assert_alive() end) end) + +describe("'inccommand' with multiple buffers", function() + local screen + + before_each(function() + clear() + screen = Screen.new(40, 17) + screen:set_default_attr_ids({ + [1] = {background = Screen.colors.Yellow1}, + [2] = {foreground = Screen.colors.Blue1, bold = true}, + [3] = {reverse = true}, + [4] = {reverse = true, bold = true} + }) + screen:attach() + exec_lua(setup_replace_cmd) + command('set cmdwinheight=10') + insert[[ + foo bar baz + bar baz foo + baz foo bar + ]] + command('vsplit | enew') + insert[[ + bar baz foo + baz foo bar + foo bar baz + ]] + end) + + it('works', function() + command('set inccommand=nosplit') + feed(':Replace foo bar') + screen:expect([[ + bar baz {1:bar} │ {1:bar} bar baz | + baz {1:bar} bar │ bar baz {1:bar} | + {1:bar} bar baz │ baz {1:bar} bar | + │ | + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {4:[No Name] [+] }{3:[No Name] [+] }| + :Replace foo bar^ | + ]]) + feed('<CR>') + screen:expect([[ + bar baz bar │ bar bar baz | + baz bar bar │ bar baz bar | + bar bar baz │ baz bar bar | + ^ │ | + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {4:[No Name] [+] }{3:[No Name] [+] }| + :Replace foo bar | + ]]) + end) + + it('works with inccommand=split', function() + command('set inccommand=split') + feed(':Replace foo bar') + screen:expect([[ + bar baz {1:bar} │ {1:bar} bar baz | + baz {1:bar} bar │ bar baz {1:bar} | + {1:bar} bar baz │ baz {1:bar} bar | + │ | + {4:[No Name] [+] }{3:[No Name] [+] }| + Buffer #1: | + |1| {1:bar} bar baz | + |2| bar baz {1:bar} | + |3| baz {1:bar} bar | + Buffer #2: | + |1| bar baz {1:bar} | + |2| baz {1:bar} bar | + |3| {1:bar} bar baz | + | + {2:~ }| + {3:[Preview] }| + :Replace foo bar^ | + ]]) + feed('<CR>') + screen:expect([[ + bar baz bar │ bar bar baz | + baz bar bar │ bar baz bar | + bar bar baz │ baz bar bar | + ^ │ | + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {2:~ }│{2:~ }| + {4:[No Name] [+] }{3:[No Name] [+] }| + :Replace foo bar | + ]]) + end) +end) diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index 0f4e97088c..05d55b94fb 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -321,13 +321,14 @@ describe('input non-printable chars', function() it("doesn't crash when echoing them back", function() write_file("Xtest-overwrite", [[foobar]]) 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: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, reverse = true}; + } screen:attach() - command("set display-=msgsep shortmess-=F") + command("set shortmess-=F") feed_command("e Xtest-overwrite") screen:expect([[ @@ -346,11 +347,11 @@ describe('input non-printable chars', function() write_file("Xtest-overwrite", [[smurf]]) feed_command("w") screen:expect([[ + foobar | {1:~ }| {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| + {4: }| "Xtest-overwrite" | {2:WARNING: The file has been changed since reading it!!!} | {3:Do you really want to write to it (y/n)?}^ | @@ -358,10 +359,10 @@ describe('input non-printable chars', function() feed("u") screen:expect([[ + foobar | {1:~ }| {1:~ }| - {1:~ }| - {1:~ }| + {4: }| "Xtest-overwrite" | {2:WARNING: The file has been changed since reading it!!!} | {3:Do you really want to write to it (y/n)?}u | @@ -370,9 +371,9 @@ describe('input non-printable chars', function() feed("\005") screen:expect([[ + foobar | {1:~ }| - {1:~ }| - {1:~ }| + {4: }| "Xtest-overwrite" | {2:WARNING: The file has been changed since reading it!!!} | {3:Do you really want to write to it (y/n)?}u | @@ -382,8 +383,8 @@ describe('input non-printable chars', function() feed("n") screen:expect([[ - {1:~ }| - {1:~ }| + foobar | + {4: }| "Xtest-overwrite" | {2:WARNING: The file has been changed since reading it!!!} | {3:Do you really want to write to it (y/n)?}u | diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index e7eaedba2d..522c9ccba2 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -1077,10 +1077,10 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim ]]} end) - it('redraws NOT_VALID correctly after message', function() - -- edge case: only one window was set NOT_VALID. Original report + it('redraws UPD_NOT_VALID correctly after message', function() + -- edge case: only one window was set UPD_NOT_VALID. Original report -- used :make, but fake it using one command to set the current - -- window NOT_VALID and another to show a long message. + -- window UPD_NOT_VALID and another to show a long message. command("set more") feed(':new<cr><c-w><c-w>') screen:expect{grid=[[ @@ -1243,6 +1243,19 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim | ]]) end) + + it('echo messages are shown correctly when getchar() immediately follows', function() + feed([[:echo 'foo' | echo 'bar' | call getchar()<CR>]]) + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {3: }| + foo | + bar^ | + ]]) + end) end) describe('ui/ext_messages', function() diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index e389b7ab89..9896b11218 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -32,7 +32,7 @@ describe('ui/mouse/input', function() [6] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, [7] = {bold = true, foreground = Screen.colors.SeaGreen4}, }) - command("set display-=msgsep mousemodel=extend") + command("set mousemodel=extend") feed('itesting<cr>mouse<cr>support and selection<esc>') screen:expect([[ testing | diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua index b30aa67fd3..78a1e8c677 100644 --- a/test/functional/ui/multigrid_spec.lua +++ b/test/functional/ui/multigrid_spec.lua @@ -3,7 +3,9 @@ local Screen = require('test.functional.ui.screen') local clear = helpers.clear local feed, command, insert = helpers.feed, helpers.command, helpers.insert local eq = helpers.eq +local funcs = helpers.funcs local meths = helpers.meths +local curwin = helpers.curwin local poke_eventloop = helpers.poke_eventloop @@ -871,6 +873,15 @@ describe('ext_multigrid', function() before_each(function() screen:try_resize_grid(2, 60, 20) end) + + it('winwidth() winheight() getwininfo() return inner width and height #19743', function() + eq(60, funcs.winwidth(0)) + eq(20, funcs.winheight(0)) + local win_info = funcs.getwininfo(curwin().id)[1] + eq(60, win_info.width) + eq(20, win_info.height) + end) + it('gets written till grid width', function() insert(('a'):rep(60).."\n") diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index 71c6410013..9bb067ed8e 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -100,45 +100,52 @@ describe("shell command :!", function() pending('missing printf') end local screen = Screen.new(50, 4) + screen:set_default_attr_ids { + [1] = {bold = true, reverse = true}; + [2] = {bold = true, foreground = Screen.colors.SeaGreen}; + [3] = {foreground = Screen.colors.Blue}; + } screen:attach() - command("set display-=msgsep") -- Print TAB chars. #2958 feed([[:!printf '1\t2\t3'<CR>]]) - screen:expect([[ - ~ | + screen:expect{grid=[[ + {1: }| :!printf '1\t2\t3' | 1 2 3 | - Press ENTER or type command to continue^ | - ]]) + {2:Press ENTER or type command to continue}^ | + ]]} feed([[<CR>]]) + -- Print BELL control code. #4338 screen.bell = false feed([[:!printf '\007\007\007\007text'<CR>]]) screen:expect{grid=[[ - ~ | + {1: }| :!printf '\007\007\007\007text' | text | - Press ENTER or type command to continue^ | + {2:Press ENTER or type command to continue}^ | ]], condition=function() eq(true, screen.bell) end} feed([[<CR>]]) + -- Print BS control code. feed([[:echo system('printf ''\010\n''')<CR>]]) screen:expect([[ - ~ | - ^H | + {1: }| + {3:^H} | | - Press ENTER or type command to continue^ | + {2: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^ | + {2:Press ENTER or type command to continue}^ | ]]) feed([[<CR>]]) end) diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 7b0005bcf1..dcd4ad3d9a 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -7,7 +7,6 @@ local insert = helpers.insert local meths = helpers.meths local command = helpers.command local funcs = helpers.funcs -local get_pathsep = helpers.get_pathsep local eq = helpers.eq local pcall_err = helpers.pcall_err local exec_lua = helpers.exec_lua @@ -1785,6 +1784,8 @@ describe('builtin popupmenu', function() screen:try_resize(32,10) command('set wildmenu') command('set wildoptions=pum') + command('set shellslash') + command("cd test/functional/fixtures/wildpum") feed(':sign ') screen:expect([[ @@ -1800,7 +1801,7 @@ describe('builtin popupmenu', function() :sign ^ | ]]) - feed('<tab>') + feed('<Tab>') screen:expect([[ | {1:~ }| @@ -1814,21 +1815,215 @@ describe('builtin popupmenu', function() :sign define^ | ]]) - feed('<left>') + feed('<Right><Right>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{n: define }{1: }| + {1:~ }{n: jump }{1: }| + {1:~ }{s: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{n: unplace }{1: }| + :sign list^ | + ]]) + + feed('<C-N>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{n: define }{1: }| + {1:~ }{n: jump }{1: }| + {1:~ }{n: list }{1: }| + {1:~ }{s: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{n: unplace }{1: }| + :sign place^ | + ]]) + + feed('<C-P>') screen:expect([[ | {1:~ }| {1:~ }| {1:~ }{n: define }{1: }| {1:~ }{n: jump }{1: }| + {1:~ }{s: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{n: unplace }{1: }| + :sign list^ | + ]]) + + feed('<Left>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{n: define }{1: }| + {1:~ }{s: jump }{1: }| {1:~ }{n: list }{1: }| {1:~ }{n: place }{1: }| {1:~ }{n: undefine }{1: }| {1:~ }{n: unplace }{1: }| + :sign jump^ | + ]]) + + -- pressing <C-E> should end completion and go back to the original match + feed('<C-E>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| :sign ^ | ]]) - feed('<left>') + -- pressing <C-Y> should select the current match and end completion + feed('<Tab><C-P><C-P><C-Y>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :sign unplace^ | + ]]) + + -- showing popup menu in different columns in the cmdline + feed('<C-U>sign define <Tab>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{s: culhl= }{1: }| + {1:~ }{n: icon= }{1: }| + {1:~ }{n: linehl= }{1: }| + {1:~ }{n: numhl= }{1: }| + {1:~ }{n: text= }{1: }| + {1:~ }{n: texthl= }{1: }| + :sign define culhl=^ | + ]]) + + feed('<Space><Tab>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{s: culhl= }{1: }| + {1:~ }{n: icon= }{1: }| + {1:~ }{n: linehl= }{1: }| + {1:~ }{n: numhl= }{1: }| + {1:~ }{n: text= }{1: }| + {1:~ }{n: texthl= }{1: }| + :sign define culhl= culhl=^ | + ]]) + + feed('<C-U>e Xdi<Tab><Tab>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }{s: XdirA/ }{1: }| + {1:~ }{n: XfileA }{1: }| + :e Xdir/XdirA/^ | + ]]) + + -- Pressing <Down> on a directory name should go into that directory + feed('<Down>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }{s: XdirB/ }{1: }| + {1:~ }{n: XfileB }{1: }| + :e Xdir/XdirA/XdirB/^ | + ]]) + + -- Pressing <Up> on a directory name should go to the parent directory + feed('<Up>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }{s: XdirA/ }{1: }| + {1:~ }{n: XfileA }{1: }| + :e Xdir/XdirA/^ | + ]]) + + -- Pressing <C-A> when the popup menu is displayed should list all the + -- matches and remove the popup menu + feed(':<C-U>sign <Tab><C-A>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4: }| + :sign define jump list place und| + efine unplace^ | + ]]) + + -- Pressing <Left> after that should move the cursor + feed('<Left>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4: }| + :sign define jump list place und| + efine unplac^e | + ]]) + feed('<End>') + + -- Pressing <C-D> when the popup menu is displayed should remove the popup + -- menu + feed('<C-U>sign <Tab><C-D>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4: }| + :sign define | + define | + :sign define^ | + ]]) + + -- Pressing <S-Tab> should open the popup menu with the last entry selected + feed('<C-U><CR>:sign <S-Tab><C-P>') screen:expect([[ | {1:~ }| @@ -1837,23 +2032,195 @@ describe('builtin popupmenu', function() {1:~ }{n: jump }{1: }| {1:~ }{n: list }{1: }| {1:~ }{n: place }{1: }| + {1:~ }{s: undefine }{1: }| + {1:~ }{n: unplace }{1: }| + :sign undefine^ | + ]]) + + -- Pressing <Esc> should close the popup menu and cancel the cmd line + feed('<C-U><CR>:sign <Tab><Esc>') + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + + -- Typing a character when the popup is open, should close the popup + feed(':sign <Tab>x') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :sign definex^ | + ]]) + + -- When the popup is open, entering the cmdline window should close the popup + feed('<C-U>sign <Tab><C-F>') + screen:expect([[ + | + {3:[No Name] }| + {1::}sign define | + {1::}sign define^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:[Command Line] }| + :sign define | + ]]) + feed(':q<CR>') + + -- After the last popup menu item, <C-N> should show the original string + feed(':sign u<Tab><C-N><C-N>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| {1:~ }{n: undefine }{1: }| - {1:~ }{s: unplace }{1: }| - :sign unplace^ | + {1:~ }{n: unplace }{1: }| + :sign u^ | ]]) - feed('x') + -- Use the popup menu for the command name + feed('<C-U>bu<Tab>') screen:expect([[ | {1:~ }| {1:~ }| {1:~ }| {1:~ }| + {s: bufdo }{1: }| + {n: buffer }{1: }| + {n: buffers }{1: }| + {n: bunload }{1: }| + :bufdo^ | + ]]) + + -- Pressing <BS> should remove the popup menu and erase the last character + feed('<C-E><C-U>sign <Tab><BS>') + screen:expect([[ + | + {1:~ }| {1:~ }| {1:~ }| {1:~ }| {1:~ }| - :sign unplacex^ | + {1:~ }| + {1:~ }| + {1:~ }| + :sign defin^ | + ]]) + + -- Pressing <C-W> should remove the popup menu and erase the previous word + feed('<C-E><C-U>sign <Tab><C-W>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :sign ^ | + ]]) + + -- Pressing <C-U> should remove the popup menu and erase the entire line + feed('<C-E><C-U>sign <Tab><C-U>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :^ | + ]]) + + -- Using <C-E> to cancel the popup menu and then pressing <Up> should recall + -- the cmdline from history + feed('sign xyz<Esc>:sign <Tab><C-E><Up>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :sign xyz^ | + ]]) + + feed('<esc>') + + -- Check "list" still works + command('set wildmode=longest,list') + feed(':cn<Tab>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {4: }| + :cn | + cnewer cnoreabbrev | + cnext cnoremap | + cnfile cnoremenu | + :cn^ | + ]]) + feed('s') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {4: }| + :cn | + cnewer cnoreabbrev | + cnext cnoremap | + cnfile cnoremenu | + :cns^ | + ]]) + + feed('<esc>') + command('set wildmode=full') + + -- Tests a directory name contained full-width characters. + feed(':e あいう/<Tab>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }{s: 123 }{1: }| + {1:~ }{n: abc }{1: }| + {1:~ }{n: xyz }{1: }| + :e あいう/123^ | ]]) feed('<esc>') @@ -1927,12 +2294,12 @@ describe('builtin popupmenu', function() :b långfile^ | ]]) - -- special case: when patterns ends with "/", show menu items aligned - -- after the "/" feed('<esc>') command("close") command('set wildmode=full') - command("cd test/functional/fixtures/") + + -- special case: when patterns ends with "/", show menu items aligned + -- after the "/" feed(':e compdir/<tab>') screen:expect([[ | @@ -1949,7 +2316,7 @@ describe('builtin popupmenu', function() {1:~ }| {1:~ }{s: file1 }{1: }| {1:~ }{n: file2 }{1: }| - :e compdir]]..get_pathsep()..[[file1^ | + :e compdir/file1^ | ]]) end) @@ -2007,7 +2374,9 @@ describe('builtin popupmenu', function() command('set wildoptions=pum') command('set wildmode=longest,full') - feed(':sign u<tab>') + -- With 'wildmode' set to 'longest,full', completing a match should display + -- the longest match, the wildmenu should not be displayed. + feed(':sign u<Tab>') screen:expect{grid=[[ | {1:~ }| @@ -2020,7 +2389,8 @@ describe('builtin popupmenu', function() ]]} eq(0, funcs.wildmenumode()) - feed('<tab>') + -- pressing <Tab> should display the wildmenu + feed('<Tab>') screen:expect{grid=[[ | {1:~ }| @@ -2032,6 +2402,19 @@ describe('builtin popupmenu', function() :sign undefine^ | ]]} eq(1, funcs.wildmenumode()) + + -- pressing <Tab> second time should select the next entry in the menu + feed('<Tab>') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }{n: undefine }{1: }| + {1:~ }{s: unplace }{1: }| + :sign unplace^ | + ]]} end) it("'pumblend' RGB-color", function() diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index ea98705394..6ee9e7b393 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -546,7 +546,7 @@ function Screen:_wait(check, flags) return true end - run_session(self._session, flags.request_cb, notification_cb, nil, minimal_timeout) + local eof = run_session(self._session, flags.request_cb, notification_cb, nil, minimal_timeout) if not did_flush then err = "no flush received" elseif not checked then @@ -557,9 +557,9 @@ function Screen:_wait(check, flags) end end - if not success_seen then + if not success_seen and not eof then did_miminal_timeout = true - run_session(self._session, flags.request_cb, notification_cb, nil, timeout-minimal_timeout) + eof = run_session(self._session, flags.request_cb, notification_cb, nil, timeout-minimal_timeout) end local did_warn = false @@ -600,8 +600,10 @@ between asynchronous (feed(), nvim_input()) and synchronous API calls. if err then + if eof then err = err..'\n\n'..eof[2] end busted.fail(err, 3) elseif did_warn then + if eof then print(eof[2]) end local tb = debug.traceback() local index = string.find(tb, '\n%s*%[C]') print(string.sub(tb,1,index)) diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua index f3735c8e4c..add5144e1b 100644 --- a/test/functional/ui/statusline_spec.lua +++ b/test/functional/ui/statusline_spec.lua @@ -394,7 +394,7 @@ describe('global statusline', function() meths.input_mouse('left', 'drag', '', 0, 14, 10) eq(1, meths.get_option('cmdheight')) meths.input_mouse('left', 'drag', '', 0, 15, 10) - eq(0, meths.get_option('cmdheight')) + eq(1, meths.get_option('cmdheight')) meths.input_mouse('left', 'drag', '', 0, 14, 10) eq(1, meths.get_option('cmdheight')) end) diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index 98398bc7a1..58ffa3bda8 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -461,20 +461,20 @@ end) describe('command line completion', function() local screen before_each(function() + clear() screen = Screen.new(40, 5) screen:set_default_attr_ids({ [1] = {bold = true, foreground = Screen.colors.Blue1}, [2] = {foreground = Screen.colors.Grey0, background = Screen.colors.Yellow}, [3] = {bold = true, reverse = true}, }) + screen:attach() end) after_each(function() os.remove('Xtest-functional-viml-compl-dir') end) it('lists directories with empty PATH', function() - clear() - screen:attach() local tmp = funcs.tempname() command('e '.. tmp) command('cd %:h') @@ -491,8 +491,6 @@ describe('command line completion', function() end) it('completes env var names #9681', function() - clear() - screen:attach() command('let $XTEST_1 = "foo" | let $XTEST_2 = "bar"') command('set wildmenu wildmode=full') feed(':!echo $XTEST_<tab>') @@ -521,6 +519,58 @@ describe('command line completion', function() :!echo $XTEST_1AaあB^ | ]]) end) + + it('does not leak memory with <S-Tab> with wildmenu and only one match #19874', function() + meths.set_option('wildmenu', true) + meths.set_option('wildmode', 'full') + meths.set_option('wildoptions', 'pum') + + feed(':sign unpla<S-Tab>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + :sign unplace^ | + ]]) + + feed('<Space>buff<Tab>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + :sign unplace buffer=^ | + ]]) + end) + + it('does not show matches with <S-Tab> without wildmenu with wildmode=full', function() + meths.set_option('wildmenu', false) + meths.set_option('wildmode', 'full') + + feed(':sign <S-Tab>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + :sign unplace^ | + ]]) + end) + + it('shows matches with <S-Tab> without wildmenu with wildmode=list', function() + meths.set_option('wildmenu', false) + meths.set_option('wildmode', 'list') + + feed(':sign <S-Tab>') + screen:expect([[ + {3: }| + :sign define | + define list undefine | + jump place unplace | + :sign unplace^ | + ]]) + end) end) describe('ui/ext_wildmenu', function() diff --git a/test/functional/ui/winbar_spec.lua b/test/functional/ui/winbar_spec.lua index 60fa10da87..ece27ec3ff 100644 --- a/test/functional/ui/winbar_spec.lua +++ b/test/functional/ui/winbar_spec.lua @@ -7,6 +7,8 @@ local meths = helpers.meths local eq = helpers.eq local poke_eventloop = helpers.poke_eventloop local feed = helpers.feed +local funcs = helpers.funcs +local curwin = helpers.curwin local pcall_err = helpers.pcall_err describe('winbar', function() @@ -48,6 +50,11 @@ describe('winbar', function() {3:~ }| | ]]) + -- winbar is excluded from the heights returned by winheight() and getwininfo() + eq(11, funcs.winheight(0)) + local win_info = funcs.getwininfo(curwin().id)[1] + eq(11, win_info.height) + eq(1, win_info.winbar) end) it('works with custom \'fillchars\' value', function() @@ -579,47 +586,95 @@ describe('winbar', function() end) end) -it('local winbar works with tabs', function() - clear() - local screen = Screen.new(60, 13) - screen:attach() - screen:set_default_attr_ids({ - [1] = {bold = true}, - [2] = {reverse = true}, - [3] = {bold = true, foreground = Screen.colors.Blue}, - [4] = {underline = true, background = Screen.colors.LightGray} - }) - meths.set_option_value('winbar', 'foo', { scope = 'local', win = 0 }) - command('tabnew') - screen:expect([[ - {4: [No Name] }{1: [No Name] }{2: }{4:X}| - ^ | - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - | - ]]) - command('tabnext') - screen:expect{grid=[[ - {1: [No Name] }{4: [No Name] }{2: }{4:X}| - {1:foo }| - ^ | - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - | - ]]} +describe('local winbar with tabs', function() + local screen + before_each(function() + clear() + screen = Screen.new(60, 10) + screen:attach() + screen:set_default_attr_ids({ + [1] = {bold = true}, + [2] = {reverse = true}, + [3] = {bold = true, foreground = Screen.colors.Blue}, + [4] = {underline = true, background = Screen.colors.LightGray} + }) + meths.set_option_value('winbar', 'foo', { scope = 'local', win = 0 }) + end) + + it('works', function() + command('tabnew') + screen:expect([[ + {4: [No Name] }{1: [No Name] }{2: }{4:X}| + ^ | + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + | + ]]) + command('tabnext') + screen:expect{grid=[[ + {1: [No Name] }{4: [No Name] }{2: }{4:X}| + {1:foo }| + ^ | + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + | + ]]} + end) + + it('can edit new empty buffer #19458', function() + insert [[ + some + goofy + text]] + screen:expect{grid=[[ + {1:foo }| + some | + goofy | + tex^t | + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + | + ]]} + + -- this used to throw an E315 ml_get error + command 'tabedit' + screen:expect{grid=[[ + {4: + [No Name] }{1: [No Name] }{2: }{4:X}| + ^ | + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + | + ]]} + + command 'tabprev' + screen:expect{grid=[[ + {1: + [No Name] }{4: [No Name] }{2: }{4:X}| + {1:foo }| + some | + goofy | + tex^t | + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + | + ]]} + end) end) diff --git a/test/functional/vimscript/eval_spec.lua b/test/functional/vimscript/eval_spec.lua index 0c2ca8de78..d4fa7afe89 100644 --- a/test/functional/vimscript/eval_spec.lua +++ b/test/functional/vimscript/eval_spec.lua @@ -10,11 +10,13 @@ -- test/functional/vimscript/functions_spec.lua local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') local lfs = require('lfs') local clear = helpers.clear local eq = helpers.eq local exc_exec = helpers.exc_exec +local exec = helpers.exec local eval = helpers.eval local command = helpers.command local write_file = helpers.write_file @@ -144,3 +146,104 @@ describe('List support code', function() end end) end) + +-- oldtest: Test_deep_nest() +it('Error when if/for/while/try/function is nested too deep',function() + clear() + local screen = Screen.new(80, 24) + screen:attach() + meths.set_option('laststatus', 2) + exec([[ + " Deep nesting of if ... endif + func Test1() + let @a = join(repeat(['if v:true'], 51), "\n") + let @a ..= "\n" + let @a ..= join(repeat(['endif'], 51), "\n") + @a + let @a = '' + endfunc + + " Deep nesting of for ... endfor + func Test2() + let @a = join(repeat(['for i in [1]'], 51), "\n") + let @a ..= "\n" + let @a ..= join(repeat(['endfor'], 51), "\n") + @a + let @a = '' + endfunc + + " Deep nesting of while ... endwhile + func Test3() + let @a = join(repeat(['while v:true'], 51), "\n") + let @a ..= "\n" + let @a ..= join(repeat(['endwhile'], 51), "\n") + @a + let @a = '' + endfunc + + " Deep nesting of try ... endtry + func Test4() + let @a = join(repeat(['try'], 51), "\n") + let @a ..= "\necho v:true\n" + let @a ..= join(repeat(['endtry'], 51), "\n") + @a + let @a = '' + endfunc + + " Deep nesting of function ... endfunction + func Test5() + let @a = join(repeat(['function X()'], 51), "\n") + let @a ..= "\necho v:true\n" + let @a ..= join(repeat(['endfunction'], 51), "\n") + @a + let @a = '' + endfunc + ]]) + screen:expect({any = '%[No Name%]'}) + feed(':call Test1()<CR>') + screen:expect({any = 'E579: '}) + feed('<C-C>') + screen:expect({any = '%[No Name%]'}) + feed(':call Test2()<CR>') + screen:expect({any = 'E585: '}) + feed('<C-C>') + screen:expect({any = '%[No Name%]'}) + feed(':call Test3()<CR>') + screen:expect({any = 'E585: '}) + feed('<C-C>') + screen:expect({any = '%[No Name%]'}) + feed(':call Test4()<CR>') + screen:expect({any = 'E601: '}) + feed('<C-C>') + screen:expect({any = '%[No Name%]'}) + feed(':call Test5()<CR>') + screen:expect({any = 'E1058: '}) +end) + +describe("uncaught exception", function() + before_each(clear) + after_each(function() + os.remove('throw1.vim') + os.remove('throw2.vim') + os.remove('throw3.vim') + end) + + it('is not forgotten #13490', function() + command('autocmd BufWinEnter * throw "i am error"') + eq('i am error', exc_exec('try | new | endtry')) + + -- Like Vim, throwing here aborts the processing of the script, but does not stop :runtime! + -- from processing the others. + -- Only the first thrown exception should be rethrown from the :try below, though. + for i = 1, 3 do + write_file('throw' .. i .. '.vim', ([[ + let result ..= '%d' + throw 'throw%d' + let result ..= 'X' + ]]):format(i, i)) + end + command('set runtimepath+=. | let result = ""') + eq('throw1', exc_exec('try | runtime! throw*.vim | endtry')) + eq('123', eval('result')) + end) +end) diff --git a/test/functional/vimscript/input_spec.lua b/test/functional/vimscript/input_spec.lua index 554d15e550..f50b39c2c5 100644 --- a/test/functional/vimscript/input_spec.lua +++ b/test/functional/vimscript/input_spec.lua @@ -8,7 +8,8 @@ local clear = helpers.clear local source = helpers.source local command = helpers.command local exc_exec = helpers.exc_exec -local nvim_async = helpers.nvim_async +local pcall_err = helpers.pcall_err +local async_meths = helpers.async_meths local NIL = helpers.NIL local screen @@ -449,6 +450,78 @@ describe('inputdialog()', function() end) describe('confirm()', function() + -- oldtest: Test_confirm() + it('works', function() + meths.set_option('more', false) -- Avoid hit-enter prompt + meths.set_option('laststatus', 2) + -- screen:expect() calls are needed to avoid feeding input too early + screen:expect({any = '%[No Name%]'}) + + async_meths.command([[let a = confirm('Press O to proceed')]]) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('o') + screen:expect({any = '%[No Name%]'}) + eq(1, meths.get_var('a')) + + async_meths.command([[let a = 'Are you sure?'->confirm("&Yes\n&No")]]) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('y') + screen:expect({any = '%[No Name%]'}) + eq(1, meths.get_var('a')) + + async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]]) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('n') + screen:expect({any = '%[No Name%]'}) + eq(2, meths.get_var('a')) + + -- Not possible to match Vim's CTRL-C test here as CTRL-C always sets got_int in Nvim. + + -- confirm() should return 0 when pressing ESC. + async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]]) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('<Esc>') + screen:expect({any = '%[No Name%]'}) + eq(0, meths.get_var('a')) + + -- Default choice is returned when pressing <CR>. + async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]]) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('<CR>') + screen:expect({any = '%[No Name%]'}) + eq(1, meths.get_var('a')) + + async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No", 2)]]) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('<CR>') + screen:expect({any = '%[No Name%]'}) + eq(2, meths.get_var('a')) + + async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No", 0)]]) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('<CR>') + screen:expect({any = '%[No Name%]'}) + eq(0, meths.get_var('a')) + + -- Test with the {type} 4th argument + for _, type in ipairs({'Error', 'Question', 'Info', 'Warning', 'Generic'}) do + async_meths.command(([[let a = confirm('Are you sure?', "&Yes\n&No", 1, '%s')]]):format(type)) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('y') + screen:expect({any = '%[No Name%]'}) + eq(1, meths.get_var('a')) + end + + eq('Vim(call):E730: using List as a String', + pcall_err(command, 'call confirm([])')) + eq('Vim(call):E730: using List as a String', + pcall_err(command, 'call confirm("Are you sure?", [])')) + eq('Vim(call):E745: Using a List as a Number', + pcall_err(command, 'call confirm("Are you sure?", "&Yes\n&No\n", [])')) + eq('Vim(call):E730: using List as a String', + pcall_err(command, 'call confirm("Are you sure?", "&Yes\n&No\n", 0, [])')) + end) + it("shows dialog even if :silent #8788", function() command("autocmd BufNewFile * call confirm('test')") @@ -483,7 +556,7 @@ describe('confirm()', function() feed(':call nvim_command("edit x")<cr>') check_and_clear(':call nvim_command("edit |\n') - nvim_async('command', 'edit x') + async_meths.command('edit x') check_and_clear(' |\n') end) end) diff --git a/test/functional/vimscript/null_spec.lua b/test/functional/vimscript/null_spec.lua index f23f00bcc5..2451da983e 100644 --- a/test/functional/vimscript/null_spec.lua +++ b/test/functional/vimscript/null_spec.lua @@ -135,7 +135,7 @@ describe('NULL', function() 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() + 0, '', function() eq({''}, curbufmeths.get_lines(0, -1, false)) end) null_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, 0) diff --git a/test/functional/vimscript/special_vars_spec.lua b/test/functional/vimscript/special_vars_spec.lua index 97a12d490d..14ccbc3827 100644 --- a/test/functional/vimscript/special_vars_spec.lua +++ b/test/functional/vimscript/special_vars_spec.lua @@ -125,9 +125,9 @@ describe('Special values', function() end) it('work with . (concat) properly', function() - eq("true", eval('"" . v:true')) - eq("null", eval('"" . v:null')) - eq("false", eval('"" . v:false')) + eq("v:true", eval('"" . v:true')) + eq("v:null", eval('"" . v:null')) + eq("v:false", eval('"" . v:false')) end) it('work with type()', function() diff --git a/test/unit/buffer_spec.lua b/test/unit/buffer_spec.lua index 204d713fb7..5dccc2f5a2 100644 --- a/test/unit/buffer_spec.lua +++ b/test/unit/buffer_spec.lua @@ -9,6 +9,7 @@ local NULL = helpers.NULL local globals = helpers.cimport("./src/nvim/globals.h") local buffer = helpers.cimport("./src/nvim/buffer.h") +local stl = helpers.cimport("./src/nvim/statusline.h") describe('buffer functions', function() @@ -228,7 +229,7 @@ describe('buffer functions', function() local fillchar = arg.fillchar or (' '):byte() local maximum_cell_count = arg.maximum_cell_count or buffer_byte_size - return buffer.build_stl_str_hl(globals.curwin, + return stl.build_stl_str_hl(globals.curwin, output_buffer, buffer_byte_size, to_cstr(pat), diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index e61b568f3a..6387f89fe4 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2992,9 +2992,9 @@ describe('typval.c', function() {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', ''}, {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', ''}, {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', ''}, - {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, - {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'true'}, - {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'false'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'v:null'}, + {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'}, + {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'}, {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', ''}, }) do -- Using to_cstr in place of Neovim allocated string, cannot @@ -3036,9 +3036,9 @@ describe('typval.c', function() {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', nil}, {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', nil}, {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', nil}, - {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, - {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'true'}, - {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'false'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'v:null'}, + {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'}, + {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'}, {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', nil}, }) do -- Using to_cstr, cannot free with tv_clear @@ -3078,9 +3078,9 @@ describe('typval.c', function() {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', ''}, {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', ''}, {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', ''}, - {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, - {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'true'}, - {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'false'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'v:null'}, + {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'}, + {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'}, {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', ''}, }) do -- Using to_cstr, cannot free with tv_clear @@ -3121,9 +3121,9 @@ describe('typval.c', function() {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', nil}, {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', nil}, {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', nil}, - {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, - {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'true'}, - {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'false'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'v:null'}, + {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'}, + {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'}, {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', nil}, }) do -- Using to_cstr, cannot free with tv_clear diff --git a/test/unit/indent_spec.lua b/test/unit/indent_spec.lua new file mode 100644 index 0000000000..ec86822b55 --- /dev/null +++ b/test/unit/indent_spec.lua @@ -0,0 +1,30 @@ +local helpers = require("test.unit.helpers")(after_each) +local itp = helpers.gen_itp(it) + +local eq = helpers.eq + +local indent = helpers.cimport("./src/nvim/indent.h") +local globals = helpers.cimport("./src/nvim/globals.h") + +describe('get_sts_value', function() + itp([[returns 'softtabstop' when it is non-negative]], function() + globals.curbuf.b_p_sts = 5 + eq(5, indent.get_sts_value()) + + globals.curbuf.b_p_sts = 0 + eq(0, indent.get_sts_value()) + end) + + itp([[returns "effective shiftwidth" when 'softtabstop' is negative]], function() + local shiftwidth = 2 + globals.curbuf.b_p_sw = shiftwidth + local tabstop = 5 + globals.curbuf.b_p_ts = tabstop + globals.curbuf.b_p_sts = -2 + eq(shiftwidth, indent.get_sts_value()) + + shiftwidth = 0 + globals.curbuf.b_p_sw = shiftwidth + eq(tabstop, indent.get_sts_value()) + end) +end) diff --git a/test/unit/option_spec.lua b/test/unit/option_spec.lua deleted file mode 100644 index b8b8a435bc..0000000000 --- a/test/unit/option_spec.lua +++ /dev/null @@ -1,52 +0,0 @@ -local helpers = require("test.unit.helpers")(after_each) -local itp = helpers.gen_itp(it) - -local to_cstr = helpers.to_cstr -local eq = helpers.eq - -local option = helpers.cimport("./src/nvim/option.h") -local globals = helpers.cimport("./src/nvim/globals.h") - -local check_ff_value = function(ff) - return option.check_ff_value(to_cstr(ff)) -end - -describe('check_ff_value', function() - - itp('views empty string as valid', function() - eq(1, check_ff_value("")) - end) - - itp('views "unix", "dos" and "mac" as valid', function() - eq(1, check_ff_value("unix")) - eq(1, check_ff_value("dos")) - eq(1, check_ff_value("mac")) - end) - - itp('views "foo" as invalid', function() - eq(0, check_ff_value("foo")) - end) -end) - -describe('get_sts_value', function() - itp([[returns 'softtabstop' when it is non-negative]], function() - globals.curbuf.b_p_sts = 5 - eq(5, option.get_sts_value()) - - globals.curbuf.b_p_sts = 0 - eq(0, option.get_sts_value()) - end) - - itp([[returns "effective shiftwidth" when 'softtabstop' is negative]], function() - local shiftwidth = 2 - globals.curbuf.b_p_sw = shiftwidth - local tabstop = 5 - globals.curbuf.b_p_ts = tabstop - globals.curbuf.b_p_sts = -2 - eq(shiftwidth, option.get_sts_value()) - - shiftwidth = 0 - globals.curbuf.b_p_sw = shiftwidth - eq(tabstop, option.get_sts_value()) - end) -end) diff --git a/test/unit/optionstr_spec.lua b/test/unit/optionstr_spec.lua new file mode 100644 index 0000000000..f8122f4fe0 --- /dev/null +++ b/test/unit/optionstr_spec.lua @@ -0,0 +1,28 @@ +local helpers = require("test.unit.helpers")(after_each) +local itp = helpers.gen_itp(it) + +local to_cstr = helpers.to_cstr +local eq = helpers.eq + +local option = helpers.cimport("./src/nvim/optionstr.h") + +local check_ff_value = function(ff) + return option.check_ff_value(to_cstr(ff)) +end + +describe('check_ff_value', function() + + itp('views empty string as valid', function() + eq(1, check_ff_value("")) + end) + + itp('views "unix", "dos" and "mac" as valid', function() + eq(1, check_ff_value("unix")) + eq(1, check_ff_value("dos")) + eq(1, check_ff_value("mac")) + end) + + itp('views "foo" as invalid', function() + eq(0, check_ff_value("foo")) + end) +end) diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua index fb476397e6..eb23a3cff1 100644 --- a/test/unit/path_spec.lua +++ b/test/unit/path_spec.lua @@ -640,6 +640,10 @@ describe('path.c', function() eq(2, path_with_url([[test-abc:\\xyz\foo\b3]])) eq(0, path_with_url([[-test://xyz/foo/b4]])) eq(0, path_with_url([[test-://xyz/foo/b5]])) + eq(1, path_with_url([[test-C:/xyz/foo/b5]])) + eq(1, path_with_url([[test-custom:/xyz/foo/b5]])) + eq(0, path_with_url([[c:/xyz/foo/b5]])) + eq(0, path_with_url([[C:/xyz/foo/b5]])) end) end) end) |