diff options
Diffstat (limited to 'test')
26 files changed, 1406 insertions, 158 deletions
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 3db44f3f11..6926022ee3 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -193,6 +193,44 @@ describe('API', function() eq('', nvim('exec', 'echo', true)) eq('foo 42', nvim('exec', 'echo "foo" 42', true)) end) + + it('displays messages when output=false', function() + local screen = Screen.new(40, 8) + screen:attach() + screen:set_default_attr_ids({ + [0] = {bold=true, foreground=Screen.colors.Blue}, + }) + meths.exec("echo 'hello'", false) + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + hello | + ]]} + end) + + it('does\'t display messages when output=true', function() + local screen = Screen.new(40, 8) + screen:attach() + screen:set_default_attr_ids({ + [0] = {bold=true, foreground=Screen.colors.Blue}, + }) + meths.exec("echo 'hello'", true) + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + end) end) describe('nvim_command', function() diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 7471f50dbd..ceeb84cec9 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -347,4 +347,44 @@ describe('API/win', function() eq('', funcs.getcmdwintype()) end) end) + + describe('hide', function() + it('can hide current window', function() + local oldwin = meths.get_current_win() + command('split') + local newwin = meths.get_current_win() + meths.win_hide(newwin) + eq({oldwin}, meths.list_wins()) + end) + it('can hide noncurrent window', function() + local oldwin = meths.get_current_win() + command('split') + local newwin = meths.get_current_win() + meths.win_hide(oldwin) + eq({newwin}, meths.list_wins()) + end) + it('does not close the buffer', function() + local oldwin = meths.get_current_win() + local oldbuf = meths.get_current_buf() + local buf = meths.create_buf(true, false) + local newwin = meths.open_win(buf, true, { + relative='win', row=3, col=3, width=12, height=3 + }) + meths.win_hide(newwin) + eq({oldwin}, meths.list_wins()) + eq({oldbuf, buf}, meths.list_bufs()) + end) + it('deletes the buffer when bufhidden=wipe', function() + local oldwin = meths.get_current_win() + local oldbuf = meths.get_current_buf() + local buf = meths.create_buf(true, false) + local newwin = meths.open_win(buf, true, { + relative='win', row=3, col=3, width=12, height=3 + }) + meths.buf_set_option(buf, 'bufhidden', 'wipe') + meths.win_hide(newwin) + eq({oldwin}, meths.list_wins()) + eq({oldbuf}, meths.list_bufs()) + end) + end) end) diff --git a/test/functional/autoread/focus_spec.lua b/test/functional/autoread/focus_spec.lua index 1d52e9948f..3f9a0ad09b 100644 --- a/test/functional/autoread/focus_spec.lua +++ b/test/functional/autoread/focus_spec.lua @@ -9,6 +9,7 @@ local feed_data = thelpers.feed_data if helpers.pending_win32(pending) then return end describe('autoread TUI FocusGained/FocusLost', function() + local f1 = 'xtest-foo' local screen before_each(function() @@ -17,8 +18,12 @@ describe('autoread TUI FocusGained/FocusLost', function() ..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler"]') end) + teardown(function() + os.remove(f1) + end) + it('external file change', function() - local path = 'xtest-foo' + local path = f1 local expected_addition = [[ line 1 line 2 diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index b59d87eb12..9de0d08e79 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -963,8 +963,10 @@ describe('jobs', function() return rv end + local j local function send(str) - nvim('command', 'call jobsend(j, "'..str..'")') + -- check no nvim_chan_free double free with pty job (#14198) + meths.chan_send(j, str) end before_each(function() @@ -979,6 +981,7 @@ describe('jobs', function() nvim('command', 'let g:job_opts.pty = 1') nvim('command', 'let exec = [expand("<cfile>:p")]') nvim('command', "let j = jobstart(exec, g:job_opts)") + j = eval'j' eq('tty ready', next_chunk()) end) diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index fa8f7d873f..d403fbc878 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -14,7 +14,8 @@ describe('NULL', function() clear() command('let L = v:_null_list') command('let D = v:_null_dict') - command('let S = $XXX_NONEXISTENT_VAR_XXX') + command('let S = v:_null_string') + command('let V = $XXX_NONEXISTENT_VAR_XXX') end) local tmpfname = 'Xtest-functional-viml-null' after_each(function() @@ -129,6 +130,7 @@ describe('NULL', function() null_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, 0) null_test('is accepted by :cexpr', 'cexpr L', 0) null_test('is accepted by :lexpr', 'lexpr L', 0) + null_expr_test('does not crash execute()', 'execute(L)', 0, '') end) describe('dict', function() it('does not crash when indexing NULL dict', function() @@ -142,4 +144,26 @@ describe('NULL', function() null_expr_test('makes map() return v:_null_dict', 'map(D, "v:val") is# D', 0, 1) null_expr_test('makes filter() return v:_null_dict', 'filter(D, "1") is# D', 0, 1) end) + describe('string', function() + null_test('does not crash :echomsg', 'echomsg S', 0) + null_test('does not crash :execute', 'execute S', 0) + null_expr_test('does not crash execute()', 'execute(S)', 0, '') + null_expr_test('makes executable() error out', 'executable(S)', 'E928: String required', 0) + null_expr_test('does not crash filereadable()', 'filereadable(S)', 0, 0) + null_expr_test('does not crash filewritable()', 'filewritable(S)', 0, 0) + null_expr_test('does not crash fnamemodify()', 'fnamemodify(S, S)', 0, '') + null_expr_test('does not crash getfperm()', 'getfperm(S)', 0, '') + null_expr_test('does not crash getfsize()', 'getfsize(S)', 0, -1) + null_expr_test('does not crash getftime()', 'getftime(S)', 0, -1) + null_expr_test('does not crash getftype()', 'getftype(S)', 0, '') + null_expr_test('does not crash glob()', 'glob(S)', 0, '') + null_expr_test('does not crash globpath()', 'globpath(S, S)', 0, '') + null_expr_test('does not crash mkdir()', 'mkdir(S)', 0, 0) + null_expr_test('does not crash sort()', 'sort(["b", S, "a"])', 0, {'', 'a', 'b'}) + null_expr_test('does not crash split()', 'split(S)', 0, {}) + + null_test('can be used to set an option', 'let &grepprg = S', 0) + + null_expr_test('is equal to non-existent variable', 'S == V', 0, 1) + end) end) diff --git a/test/functional/eval/writefile_spec.lua b/test/functional/eval/writefile_spec.lua index 0bb7523d7e..356680ba7c 100644 --- a/test/functional/eval/writefile_spec.lua +++ b/test/functional/eval/writefile_spec.lua @@ -59,6 +59,16 @@ describe('writefile()', function() eq('\n', read_file(fname)) end) + it('writes list with a null string to a file', function() + eq(0, exc_exec( + ('call writefile([v:_null_string], "%s", "b")'):format( + fname))) + eq('', read_file(fname)) + eq(0, exc_exec(('call writefile([v:_null_string], "%s")'):format( + fname))) + eq('\n', read_file(fname)) + end) + it('appends to a file', function() eq(nil, read_file(fname)) eq(0, funcs.writefile({'abc', 'def', 'ghi'}, fname)) diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index 5c67431221..e5c9a20db3 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -371,4 +371,79 @@ describe('VimL dictionary notifications', function() eq(1, eval('g:called')) end) + it('does not crash when using dictwatcherdel in callback', function() + source([[ + let g:d = {} + + function! W1(...) + " Delete current and following watcher. + call dictwatcherdel(g:d, '*', function('W1')) + call dictwatcherdel(g:d, '*', function('W2')) + try + call dictwatcherdel({}, 'meh', function('tr')) + catch + let g:exc = v:exception + endtry + endfunction + call dictwatcheradd(g:d, '*', function('W1')) + + function! W2(...) + endfunction + call dictwatcheradd(g:d, '*', function('W2')) + + let g:d.foo = 23 + ]]) + eq(23, eval('g:d.foo')) + eq("Vim(call):Couldn't find a watcher matching key and callback", eval('g:exc')) + end) + + it('does not call watcher added in callback', function() + source([[ + let g:d = {} + let g:calls = [] + + function! W1(...) abort + call add(g:calls, 'W1') + call dictwatcheradd(g:d, '*', function('W2')) + endfunction + + function! W2(...) abort + call add(g:calls, 'W2') + endfunction + + call dictwatcheradd(g:d, '*', function('W1')) + let g:d.foo = 23 + ]]) + eq(23, eval('g:d.foo')) + eq({"W1"}, eval('g:calls')) + end) + + it('calls watcher deleted in callback', function() + source([[ + let g:d = {} + let g:calls = [] + + function! W1(...) abort + call add(g:calls, "W1") + call dictwatcherdel(g:d, '*', function('W2')) + endfunction + + function! W2(...) abort + call add(g:calls, "W2") + endfunction + + call dictwatcheradd(g:d, '*', function('W1')) + call dictwatcheradd(g:d, '*', function('W2')) + let g:d.foo = 123 + + unlet g:d + let g:d = {} + call dictwatcheradd(g:d, '*', function('W2')) + call dictwatcheradd(g:d, '*', function('W1')) + let g:d.foo = 123 + ]]) + eq(123, eval('g:d.foo')) + eq({"W1", "W2", "W2", "W1"}, eval('g:calls')) + end) + end) diff --git a/test/functional/ex_cmds/excmd_spec.lua b/test/functional/ex_cmds/excmd_spec.lua index aac2a9f469..33794eb50d 100644 --- a/test/functional/ex_cmds/excmd_spec.lua +++ b/test/functional/ex_cmds/excmd_spec.lua @@ -24,8 +24,6 @@ describe('Ex cmds', function() pcall_err(command, ':menu 9999999999999999999999999999999999999999')) eq('Vim(bdelete):E939: Positive count required', pcall_err(command, ':bdelete 9999999999999999999999999999999999999999')) - eq('Vim(retab):E487: Argument must be positive', - pcall_err(command, ':retab 9999999999999999999999999999999999999999')) assert_alive() end) end) diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua index 7cc31dc787..c9c004eec1 100644 --- a/test/functional/legacy/011_autocommands_spec.lua +++ b/test/functional/legacy/011_autocommands_spec.lua @@ -70,7 +70,7 @@ describe('file reading, writing and bufnew and filter autocommands', function() feed_command('let $GZIP = ""') --execute('au FileChangedShell * echo "caught FileChangedShell"') feed_command('set bin') - feed_command("au FileReadPost *.gz '[,']!gzip -d") + feed_command("au FileReadPost *.gz '[,']!GZIP= gzip -d") -- Read and decompress the testfile. feed_command('$r Xtestfile.gz') expect('\n'..text1) diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua index f2ced8942d..3d2c4a7d91 100644 --- a/test/functional/legacy/delete_spec.lua +++ b/test/functional/legacy/delete_spec.lua @@ -17,33 +17,6 @@ describe('Test for delete()', function() eq(-1, eval("delete('Xfile')")) end) - it('directory delete', function() - command("call mkdir('Xdir1')") - eq(1, eval("isdirectory('Xdir1')")) - eq(0, eval("delete('Xdir1', 'd')")) - eq(0, eval("isdirectory('Xdir1')")) - eq(-1, eval("delete('Xdir1', 'd')")) - end) - it('recursive delete', function() - command("call mkdir('Xdir1')") - command("call mkdir('Xdir1/subdir')") - command("call mkdir('Xdir1/empty')") - command('split Xdir1/Xfile') - command("call setline(1, ['a', 'b'])") - command('w') - command('w Xdir1/subdir/Xfile') - command('close') - - eq(1, eval("isdirectory('Xdir1')")) - eq(eval("['a', 'b']"), eval("readfile('Xdir1/Xfile')")) - eq(1, eval("isdirectory('Xdir1/subdir')")) - eq(eval("['a', 'b']"), eval("readfile('Xdir1/subdir/Xfile')")) - eq(1, eval("isdirectory('Xdir1/empty')")) - eq(0, eval("delete('Xdir1', 'rf')")) - eq(0, eval("isdirectory('Xdir1')")) - eq(-1, eval("delete('Xdir1', 'd')")) - end) - it('symlink delete', function() source([[ split Xfile @@ -63,55 +36,4 @@ describe('Test for delete()', function() eq(-1, eval("delete('Xlink')")) eq(0, eval("delete('Xfile')")) end) - - it('symlink directory delete', function() - command("call mkdir('Xdir1')") - if helpers.iswin() then - command("silent !mklink /j Xlink Xdir1") - else - command("silent !ln -s Xdir1 Xlink") - end - eq(1, eval("isdirectory('Xdir1')")) - eq(1, eval("isdirectory('Xlink')")) - -- Delete the link, not the directory - eq(0, eval("delete('Xlink')")) - eq(-1, eval("delete('Xlink')")) - eq(0, eval("delete('Xdir1', 'd')")) - end) - - it('symlink recursive delete', function() - source([[ - call mkdir('Xdir3') - call mkdir('Xdir3/subdir') - call mkdir('Xdir4') - split Xdir3/Xfile - call setline(1, ['a', 'b']) - w - w Xdir3/subdir/Xfile - w Xdir4/Xfile - close - if has('win32') - silent !mklink /j Xdir3\Xlink Xdir4 - else - silent !ln -s ../Xdir4 Xdir3/Xlink - endif - ]]) - - eq(1, eval("isdirectory('Xdir3')")) - eq(eval("['a', 'b']"), eval("readfile('Xdir3/Xfile')")) - eq(1, eval("isdirectory('Xdir3/subdir')")) - eq(eval("['a', 'b']"), eval("readfile('Xdir3/subdir/Xfile')")) - eq(1, eval("isdirectory('Xdir4')")) - eq(1, eval("isdirectory('Xdir3/Xlink')")) - eq(eval("['a', 'b']"), eval("readfile('Xdir4/Xfile')")) - - eq(0, eval("delete('Xdir3', 'rf')")) - eq(0, eval("isdirectory('Xdir3')")) - eq(-1, eval("delete('Xdir3', 'd')")) - -- symlink is deleted, not the directory it points to - eq(1, eval("isdirectory('Xdir4')")) - eq(eval("['a', 'b']"), eval("readfile('Xdir4/Xfile')")) - eq(0, eval("delete('Xdir4/Xfile')")) - eq(0, eval("delete('Xdir4', 'd')")) - end) end) diff --git a/test/functional/legacy/packadd_spec.lua b/test/functional/legacy/packadd_spec.lua index 609f825177..d92409059e 100644 --- a/test/functional/legacy/packadd_spec.lua +++ b/test/functional/legacy/packadd_spec.lua @@ -20,6 +20,7 @@ describe('packadd', function() func SetUp() let s:topdir = expand(getcwd() . '/Xdir') + call delete(s:topdir, 'rf') exe 'set packpath=' . s:topdir let s:plugdir = expand(s:topdir . '/pack/mine/opt/mytest') endfunc @@ -267,6 +268,8 @@ describe('packadd', function() call assert_match('look-here', tags1[0]) let tags2 = readfile(docdir2 . '/tags') call assert_match('look-away', tags2[0]) + + call assert_fails('helptags abcxyz', 'E150:') endfunc func Test_colorscheme() diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index 6d7d9b4d8b..5bfab1d52d 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -461,6 +461,36 @@ describe('lua: nvim_buf_attach on_bytes', function() } end) + it("deleting lines", function() + local check_events = setup_eventcheck(verify, origlines) + + feed("dd") + + check_events { + { "test1", "bytes", 1, 3, 0, 0, 0, 1, 0, 16, 0, 0, 0 }; + } + + feed("d2j") + + check_events { + { "test1", "bytes", 1, 4, 0, 0, 0, 3, 0, 48, 0, 0, 0 }; + } + + feed("ld<c-v>2j") + + check_events { + { "test1", "bytes", 1, 5, 0, 1, 1, 0, 1, 1, 0, 0, 0 }; + { "test1", "bytes", 1, 5, 1, 1, 16, 0, 1, 1, 0, 0, 0 }; + { "test1", "bytes", 1, 5, 2, 1, 31, 0, 1, 1, 0, 0, 0 }; + } + + feed("vjwd") + + check_events { + { "test1", "bytes", 1, 10, 0, 1, 1, 1, 9, 23, 0, 0, 0 }; + } + end) + it("changing lines", function() local check_events = setup_eventcheck(verify, origlines) @@ -537,20 +567,65 @@ describe('lua: nvim_buf_attach on_bytes', function() end) it('inccomand=nosplit and substitute', function() - local check_events = setup_eventcheck(verify, {"abcde"}) + local check_events = setup_eventcheck(verify, + {"abcde", "12345"}) meths.set_option('inccommand', 'nosplit') - feed ':%s/bcd/' + -- linewise substitute + feed(':%s/bcd/') check_events { { "test1", "bytes", 1, 3, 0, 1, 1, 0, 3, 3, 0, 0, 0 }; { "test1", "bytes", 1, 5, 0, 1, 1, 0, 0, 0, 0, 3, 3 }; } - feed 'a' + feed('a') check_events { { "test1", "bytes", 1, 3, 0, 1, 1, 0, 3, 3, 0, 1, 1 }; { "test1", "bytes", 1, 5, 0, 1, 1, 0, 1, 1, 0, 3, 3 }; } + + feed("<esc>") + + -- splitting lines + feed([[:%s/abc/\r]]) + check_events { + { "test1", "bytes", 1, 3, 0, 0, 0, 0, 3, 3, 1, 0, 1 }; + { "test1", "bytes", 1, 6, 0, 0, 0, 1, 0, 1, 0, 3, 3 }; + } + + feed("<esc>") + -- multi-line regex + feed([[:%s/de\n123/a]]) + + check_events { + { "test1", "bytes", 1, 3, 0, 3, 3, 1, 3, 6, 0, 1, 1 }; + { "test1", "bytes", 1, 6, 0, 3, 3, 0, 1, 1, 1, 3, 6 }; + } + + feed("<esc>") + -- replacing with unicode + feed(":%s/b/→") + + check_events { + { "test1", "bytes", 1, 3, 0, 1, 1, 0, 1, 1, 0, 3, 3 }; + { "test1", "bytes", 1, 5, 0, 1, 1, 0, 3, 3, 0, 1, 1 }; + } + + feed("<esc>") + -- replacing with escaped characters + feed([[:%s/b/\\]]) + check_events { + { "test1", "bytes", 1, 3, 0, 1, 1, 0, 1, 1, 0, 1, 1 }; + { "test1", "bytes", 1, 5, 0, 1, 1, 0, 1, 1, 0, 1, 1 }; + } + + feed("<esc>") + -- replacing with expression register + feed([[:%s/b/\=5+5]]) + check_events { + { "test1", "bytes", 1, 3, 0, 1, 1, 0, 1, 1, 0, 2, 2 }; + { "test1", "bytes", 1, 5, 0, 1, 1, 0, 2, 2, 0, 1, 1 }; + } end) it('nvim_buf_set_text insert', function() @@ -670,7 +745,7 @@ describe('lua: nvim_buf_attach on_bytes', function() command("set noet") command("set ts=4") command("set sw=2") - command("set sts=2") + command("set sts=4") local check_events = setup_eventcheck(verify, {'asdfasdf'}) @@ -689,6 +764,7 @@ describe('lua: nvim_buf_attach on_bytes', function() { "test1", "bytes", 1, 7, 0, 0, 0, 0, 4, 4, 0, 1, 1 }, } + feed("<esc>u") check_events { { "test1", "bytes", 1, 8, 0, 0, 0, 0, 1, 1, 0, 4, 4 }, @@ -728,6 +804,29 @@ describe('lua: nvim_buf_attach on_bytes', function() { "test1", "bytes", 1, 29, 0, 0, 0, 0, 0, 0, 0, 1, 1 }; { "test1", "bytes", 1, 31, 0, 0, 0, 0, 4, 4, 0, 1, 1 }; } + + -- inserting tab after other tabs + command("set sw=4") + feed("<esc>0a<tab>") + check_events { + { "test1", "bytes", 1, 32, 0, 1, 1, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 33, 0, 2, 2, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 34, 0, 3, 3, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 35, 0, 4, 4, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 36, 0, 1, 1, 0, 4, 4, 0, 1, 1 }; + } + end) + + it("retab", function() + command("set noet") + command("set ts=4") + + local check_events = setup_eventcheck(verify, {" asdf"}) + command("retab 8") + + check_events { + { "test1", "bytes", 1, 3, 0, 0, 0, 0, 7, 7, 0, 9, 9 }; + } end) it("sends events when undoing with undofile", function() @@ -802,6 +901,86 @@ describe('lua: nvim_buf_attach on_bytes', function() end) + it(":luado", function() + local check_events = setup_eventcheck(verify, {"abc", "12345"}) + + command(".luado return 'a'") + + check_events { + { "test1", "bytes", 1, 3, 0, 0, 0, 0, 3, 3, 0, 1, 1 }; + } + + command("luado return 10") + + check_events { + { "test1", "bytes", 1, 4, 0, 0, 0, 0, 1, 1, 0, 2, 2 }; + { "test1", "bytes", 1, 5, 1, 0, 3, 0, 5, 5, 0, 2, 2 }; + } + + end) + + it("flushes deleted bytes on move", function() + local check_events = setup_eventcheck(verify, {"AAA", "BBB", "CCC", "DDD"}) + + feed(":.move+1<cr>") + + check_events { + { "test1", "bytes", 1, 5, 0, 0, 0, 1, 0, 4, 0, 0, 0 }; + { "test1", "bytes", 1, 5, 1, 0, 4, 0, 0, 0, 1, 0, 4 }; + } + + feed("jd2j") + + check_events { + { "test1", "bytes", 1, 6, 2, 0, 8, 2, 0, 8, 0, 0, 0 }; + } + end) + + it("block visual paste", function() + local check_events = setup_eventcheck(verify, {"AAA", + "BBB", + "CCC", + "DDD", + "EEE", + "FFF"}) + funcs.setreg("a", "___") + feed([[gg0l<c-v>3jl"ap]]) + + check_events { + { "test1", "bytes", 1, 3, 0, 1, 1, 0, 2, 2, 0, 0, 0 }; + { "test1", "bytes", 1, 3, 1, 1, 3, 0, 2, 2, 0, 0, 0 }; + { "test1", "bytes", 1, 3, 2, 1, 5, 0, 2, 2, 0, 0, 0 }; + { "test1", "bytes", 1, 3, 3, 1, 7, 0, 2, 2, 0, 0, 0 }; + { "test1", "bytes", 1, 5, 0, 1, 1, 0, 0, 0, 0, 3, 3 }; + { "test1", "bytes", 1, 6, 1, 1, 6, 0, 0, 0, 0, 3, 3 }; + { "test1", "bytes", 1, 7, 2, 1, 11, 0, 0, 0, 0, 3, 3 }; + { "test1", "bytes", 1, 8, 3, 1, 16, 0, 0, 0, 0, 3, 3 }; + } + end) + + it("nvim_buf_set_lines", function() + local check_events = setup_eventcheck(verify, {"AAA", "BBB"}) + + -- delete + meths.buf_set_lines(0, 0, 1, true, {}) + + check_events { + { "test1", "bytes", 1, 3, 0, 0, 0, 1, 0, 4, 0, 0, 0 }; + } + + -- add + meths.buf_set_lines(0, 0, 0, true, {'asdf'}) + check_events { + { "test1", "bytes", 1, 4, 0, 0, 0, 0, 0, 0, 1, 0, 5 }; + } + + -- replace + meths.buf_set_lines(0, 0, 1, true, {'asdf', 'fdsa'}) + check_events { + { "test1", "bytes", 1, 5, 0, 0, 0, 1, 0, 5, 2, 0, 10 }; + } + end) + teardown(function() os.remove "Xtest-reload" os.remove "Xtest-undofile" diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua index a78ed07876..85c67be8f9 100644 --- a/test/functional/plugin/health_spec.lua +++ b/test/functional/plugin/health_spec.lua @@ -118,7 +118,7 @@ describe('health.vim', function() Error = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, Heading = { bold=true, foreground=Screen.colors.Magenta }, Heading2 = { foreground = Screen.colors.SlateBlue }, - Bar = { foreground=Screen.colors.Purple }, + Bar = { foreground = 0x6a0dad }, Bullet = { bold=true, foreground=Screen.colors.Brown }, }) command("checkhealth foo success1") diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 6d3af115fa..2b7198bf63 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -29,6 +29,16 @@ teardown(function() os.remove(fake_lsp_logfile) end) +local function clear_notrace() + -- problem: here be dragons + -- solution: don't look for dragons to closely + clear {env={ + NVIM_LUA_NOTRACK="1"; + VIMRUNTIME=os.getenv"VIMRUNTIME"; + }} +end + + local function fake_lsp_server_setup(test_name, timeout_ms, options) exec_lua([=[ lsp = require('vim.lsp') @@ -36,6 +46,7 @@ local function fake_lsp_server_setup(test_name, timeout_ms, options) TEST_RPC_CLIENT_ID = lsp.start_client { cmd_env = { NVIM_LOG_FILE = logfile; + NVIM_LUA_NOTRACK = "1"; }; cmd = { vim.v.progpath, '-Es', '-u', 'NONE', '--headless', @@ -65,7 +76,7 @@ end local function test_rpc_server(config) if config.test_name then - clear() + clear_notrace() fake_lsp_server_setup(config.test_name, config.timeout_ms or 1e3, config.options) end local client = setmetatable({}, { @@ -120,7 +131,7 @@ end describe('LSP', function() describe('server_name specified', function() before_each(function() - clear() + clear_notrace() -- Run an instance of nvim on the file which contains our "scripts". -- Pass TEST_NAME to pick the script. local test_name = "basic_init" @@ -250,6 +261,10 @@ describe('LSP', function() end) it('should succeed with manual shutdown', function() + if 'openbsd' == helpers.uname() then + pending('hangs the build on openbsd #14028, re-enable with freeze timeout #14204') + return + end local expected_callbacks = { {NIL, "shutdown", {}, 1, NIL}; {NIL, "test", {}, 1}; @@ -314,7 +329,7 @@ describe('LSP', function() } end) it('workspace/configuration returns NIL per section if client was started without config.settings', function() - clear() + clear_notrace() fake_lsp_server_setup('workspace/configuration no settings') eq({ NIL, NIL, }, exec_lua [[ local params = { @@ -941,7 +956,7 @@ end) describe('LSP', function() before_each(function() - clear() + clear_notrace() end) local function make_edit(y_0, x_0, y_1, x_1, text) @@ -1109,14 +1124,14 @@ describe('LSP', function() '2nd line of 语text'; }, buf_lines(target_bufnr)) end) - it('correctly goes ahead with the edit if the version is vim.NIL', function() - -- we get vim.NIL when we decode json null value. - local json = exec_lua[[ - return vim.fn.json_decode("{ \"a\": 1, \"b\": null }") - ]] - eq(json.b, exec_lua("return vim.NIL")) - - exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit(exec_lua("return vim.NIL"))) + it('always accepts edit with version = 0', function() + exec_lua([[ + local args = {...} + local bufnr = select(1, ...) + local text_edit = select(2, ...) + vim.lsp.util.buf_versions[bufnr] = 10 + vim.lsp.util.apply_text_document_edit(text_edit) + ]], target_bufnr, text_document_edit(0)) eq({ 'First ↥ 🤦 🦄 line of text'; '2nd line of 语text'; @@ -1805,20 +1820,36 @@ describe('LSP', function() end) describe('lsp.util.jump_to_location', function() - local target_bufnr + local default_target_bufnr + local default_target_uri = 'file://fake/uri' - before_each(function() - target_bufnr = exec_lua [[ - local bufnr = vim.uri_to_bufnr("file://fake/uri") - local lines = {"1st line of text", "å å ɧ 汉语 ↥ 🤦 🦄"} + local create_buf = function(uri, lines) + for i, line in ipairs(lines) do + lines[i] = '"' .. line .. '"' + end + lines = table.concat(lines, ", ") + + -- Let's set "hidden" to true in order to avoid errors when switching + -- between buffers in test. + local code = string.format([[ + vim.api.nvim_set_option('hidden', true) + + local bufnr = vim.uri_to_bufnr("%s") + local lines = {%s} vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) return bufnr - ]] + ]], uri, lines) + + return exec_lua(code) + end + + before_each(function() + default_target_bufnr = create_buf(default_target_uri, {'1st line of text', 'å å ɧ 汉语 ↥ 🤦 🦄'}) end) - local location = function(start_line, start_char, end_line, end_char) + local location = function(uri, start_line, start_char, end_line, end_char) return { - uri = "file://fake/uri", + uri = uri, range = { start = { line = start_line, character = start_char }, ["end"] = { line = end_line, character = end_char }, @@ -1826,9 +1857,9 @@ describe('LSP', function() } end - local jump = function(msg) + local jump = function(bufnr, msg) eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg)) - eq(target_bufnr, exec_lua[[return vim.fn.bufnr('%')]]) + eq(bufnr, exec_lua[[return vim.fn.bufnr('%')]]) return { line = exec_lua[[return vim.fn.line('.')]], col = exec_lua[[return vim.fn.col('.')]], @@ -1836,13 +1867,13 @@ describe('LSP', function() end it('jumps to a Location', function() - local pos = jump(location(0, 9, 0, 9)) + local pos = jump(default_target_bufnr, location(default_target_uri, 0, 9, 0, 9)) eq(1, pos.line) eq(10, pos.col) end) it('jumps to a LocationLink', function() - local pos = jump({ + local pos = jump(default_target_bufnr, { targetUri = "file://fake/uri", targetSelectionRange = { start = { line = 0, character = 4 }, @@ -1858,11 +1889,105 @@ describe('LSP', function() end) it('jumps to the correct multibyte column', function() - local pos = jump(location(1, 2, 1, 2)) + local pos = jump(default_target_bufnr, location(default_target_uri, 1, 2, 1, 2)) eq(2, pos.line) eq(4, pos.col) eq('å', exec_lua[[return vim.fn.expand('<cword>')]]) end) + + it('adds current position to jumplist before jumping', function() + exec_lua([[ + vim.api.nvim_win_set_buf(0, ...) + vim.api.nvim_win_set_cursor(0, {2, 0}) + ]], default_target_bufnr) + jump(default_target_bufnr, location(default_target_uri, 0, 9, 0, 9)) + + local mark = exec_lua([[return vim.inspect(vim.api.nvim_buf_get_mark(..., "'"))]], default_target_bufnr) + eq('{ 2, 0 }', mark) + end) + + it('should not push item to tagstack if destination is the same as source', function() + -- Set cursor at the 2nd line, 1st character. This is the source position + -- for the test, and will also be the destination one, making the cursor + -- "motionless", thus not triggering a push to the tagstack. + exec_lua(string.format([[ + vim.api.nvim_win_set_buf(0, %d) + vim.api.nvim_win_set_cursor(0, {2, 0}) + ]], default_target_bufnr)) + + -- Jump to 'f' in 'foobar', at the 2nd line. + jump(default_target_bufnr, location(default_target_uri, 1, 0, 1, 0)) + + local stack = exec_lua[[return vim.fn.gettagstack()]] + eq(0, stack.length) + end) + + it('should not push the same item from same buffer twice to tagstack', function() + -- Set cursor at the 2nd line, 5th character. + exec_lua(string.format([[ + vim.api.nvim_win_set_buf(0, %d) + vim.api.nvim_win_set_cursor(0, {2, 4}) + ]], default_target_bufnr)) + + local stack + + -- Jump to 1st line, 1st column. + jump(default_target_bufnr, location(default_target_uri, 0, 0, 0, 0)) + + stack = exec_lua[[return vim.fn.gettagstack()]] + eq({default_target_bufnr, 2, 5, 0}, stack.items[1].from) + + -- Go back to 5th character at 2nd line, which is currently at the top of + -- the tagstack. + exec_lua(string.format([[ + vim.api.nvim_win_set_cursor(0, {2, 4}) + ]], default_target_bufnr)) + + -- Jump again to 1st line, 1st column. Since we're jumping from the same + -- position we have just jumped from, this jump shouldn't be pushed to + -- the tagstack. + jump(default_target_bufnr, location(default_target_uri, 0, 0, 0, 0)) + + stack = exec_lua[[return vim.fn.gettagstack()]] + eq({default_target_bufnr, 2, 5, 0}, stack.items[1].from) + eq(1, stack.length) + end) + + it('should not push the same item from another buffer twice to tagstack', function() + local target_uri = 'file://foo/bar' + local target_bufnr = create_buf(target_uri, {'this is a line', 'foobar'}) + + -- Set cursor at the 1st line, 3rd character of the default test buffer. + exec_lua(string.format([[ + vim.api.nvim_win_set_buf(0, %d) + vim.api.nvim_win_set_cursor(0, {1, 2}) + ]], default_target_bufnr)) + + local stack + + -- Jump to 1st line, 1st column of a different buffer from the source + -- position. + jump(target_bufnr, location(target_uri, 0, 0, 0, 0)) + + stack = exec_lua[[return vim.fn.gettagstack()]] + eq({default_target_bufnr, 1, 3, 0}, stack.items[1].from) + + -- Go back to 3rd character at 1st line of the default test buffer, which + -- is currently at the top of the tagstack. + exec_lua(string.format([[ + vim.api.nvim_win_set_buf(0, %d) + vim.api.nvim_win_set_cursor(0, {1, 2}) + ]], default_target_bufnr)) + + -- Jump again to 1st line, 1st column of the different buffer. Since + -- we're jumping from the same position we have just jumped from, this + -- jump shouldn't be pushed to the tagstack. + jump(target_bufnr, location(target_uri, 0, 0, 0, 0)) + + stack = exec_lua[[return vim.fn.gettagstack()]] + eq({default_target_bufnr, 1, 3, 0}, stack.items[1].from) + eq(1, stack.length) + end) end) describe('lsp.util._make_floating_popup_size', function() diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 209537831f..c61bf108cb 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -258,6 +258,13 @@ describe(':terminal buffer', function() it('handles wqall', function() eq('Vim(wqall):E948: Job still running', exc_exec('wqall')) end) + + it('does not segfault when pasting empty buffer #13955', function() + feed_command('terminal') + feed('<c-\\><c-n>') + feed_command('put a') -- buffer a is empty + helpers.assert_alive() + end) end) describe('No heap-buffer-overflow when using', function() diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index cb73bfbbe1..05e0c5fe2c 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -445,7 +445,7 @@ describe('treesitter highlighting', function() exec_lua [[ local parser = vim.treesitter.get_parser(0, "c", { - queries = {c = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"} + injections = {c = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"} }) local highlighter = vim.treesitter.highlighter test_hl = highlighter.new(parser, {queries = {c = hl_query}}) @@ -472,4 +472,102 @@ describe('treesitter highlighting', function() | ]]} end) + + it("supports overriding queries, like ", function() + if pending_c_parser(pending) then return end + + insert([[ + int x = INT_MAX; + #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + #define foo void main() { \ + return 42; \ + } + ]]) + + exec_lua [[ + local injection_query = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)" + require('vim.treesitter.query').set_query("c", "highlights", hl_query) + require('vim.treesitter.query').set_query("c", "injections", injection_query) + + vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, "c")) + ]] + + screen:expect{grid=[[ + {3:int} x = {5:INT_MAX}; | + #define {5:READ_STRING}(x, y) ({3:char_u} *)read_string((x), ({3:size_t})(y))| + #define foo {3:void} main() { \ | + {4:return} {5:42}; \ | + } | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it("supports highlighting with custom highlight groups", function() + if pending_c_parser(pending) then return end + + insert(hl_text) + + exec_lua [[ + local parser = vim.treesitter.get_parser(0, "c") + test_hl = vim.treesitter.highlighter.new(parser, {queries = {c = hl_query}}) + ]] + + 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) | + { | + {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | + || {6:lstate} != {6:lstate}) { | + {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | + {4:return} {11:lua_error}(lstate); | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + ^} | + {1:~ }| + {1:~ }| + | + ]]} + + -- 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")]] + 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) | + { | + {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | + || {6:lstate} != {6:lstate}) { | + {11:lua_pushliteral}(lstate, {2:"vim.schedule: expected function"}); | + {4:return} {11:lua_error}(lstate); | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + ^} | + {1:~ }| + {1:~ }| + | + ]]} + screen:expect{ unchanged=true } + end) end) diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index a5801271cb..afb17dd2cf 100644 --- a/test/functional/treesitter/language_spec.lua +++ b/test/functional/treesitter/language_spec.lua @@ -45,7 +45,7 @@ describe('treesitter API', function() return {keys, lang.fields, symbols} ]])) - eq({fields=true, symbols=true}, keys) + eq({fields=true, symbols=true, _abi_version=true}, keys) local fset = {} for _,f in pairs(fields) do diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index f99362fbdf..1017913709 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -235,6 +235,41 @@ void ui_refresh(void) }, res) end) + it('can match special regex characters like \\ * + ( with `vim-match?`', function() + if pending_c_parser(pending) then return end + + insert('char* astring = "\\n"; (1 + 1) * 2 != 2;') + + local res = exec_lua([[ + cquery = vim.treesitter.parse_query("c", '((_) @plus (vim-match? @plus "^\\\\+$"))'.. + '((_) @times (vim-match? @times "^\\\\*$"))'.. + '((_) @paren (vim-match? @paren "^\\\\($"))'.. + '((_) @escape (vim-match? @escape "^\\\\\\\\n$"))'.. + '((_) @string (vim-match? @string "^\\"\\\\\\\\n\\"$"))') + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + res = {} + for pattern, match in cquery:iter_matches(tree:root(), 0) do + -- can't transmit node over RPC. just check the name and range + local mrepr = {} + for cid,node in pairs(match) do + table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) + end + table.insert(res, {pattern, mrepr}) + end + return res + ]]) + + eq({ + { 2, { { "times", '*', 0, 4, 0, 5 } } }, + { 5, { { "string", 'string_literal', 0, 16, 0, 20 } } }, + { 4, { { "escape", 'escape_sequence', 0, 17, 0, 19 } } }, + { 3, { { "paren", '(', 0, 22, 0, 23 } } }, + { 1, { { "plus", '+', 0, 25, 0, 26 } } }, + { 2, { { "times", '*', 0, 30, 0, 31 } } }, + }, res) + end) + it('allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', function() if pending_c_parser(pending) then return end @@ -468,7 +503,7 @@ int x = INT_MAX; it("should inject a language", function() exec_lua([[ parser = vim.treesitter.get_parser(0, "c", { - queries = { + injections = { c = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"}}) ]]) @@ -489,7 +524,7 @@ int x = INT_MAX; it("should inject a language", function() exec_lua([[ parser = vim.treesitter.get_parser(0, "c", { - queries = { + injections = { c = "(preproc_def (preproc_arg) @c @combined) (preproc_function_def value: (preproc_arg) @c @combined)"}}) ]]) @@ -506,11 +541,39 @@ int x = INT_MAX; end) end) + describe("when providing parsing information through a directive", function() + it("should inject a language", function() + exec_lua([=[ + vim.treesitter.add_directive("inject-clang!", function(match, _, _, pred, metadata) + metadata.language = "c" + metadata.combined = true + metadata.content = pred[2] + end) + + parser = vim.treesitter.get_parser(0, "c", { + injections = { + c = "(preproc_def ((preproc_arg) @_c (#inject-clang! @_c)))" .. + "(preproc_function_def value: ((preproc_arg) @_a (#inject-clang! @_a)))"}}) + ]=]) + + eq("table", exec_lua("return type(parser:children().c)")) + eq(2, exec_lua("return #parser:children().c:trees()")) + eq({ + {0, 0, 7, 0}, -- root tree + {3, 14, 5, 18}, -- VALUE 123 + -- VALUE1 123 + -- VALUE2 123 + {1, 26, 2, 68} -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) + }, get_ranges()) + end) + end) + describe("when using the offset directive", function() it("should shift the range by the directive amount", function() exec_lua([[ parser = vim.treesitter.get_parser(0, "c", { - queries = { + injections = { c = "(preproc_def ((preproc_arg) @c (#offset! @c 0 2 0 -1))) (preproc_function_def value: (preproc_arg) @c)"}}) ]]) @@ -538,7 +601,7 @@ int x = INT_MAX; it("should return the correct language tree", function() local result = exec_lua([[ parser = vim.treesitter.get_parser(0, "c", { - queries = { c = "(preproc_def (preproc_arg) @c)"}}) + injections = { c = "(preproc_def (preproc_arg) @c)"}}) local sub_tree = parser:language_for_range({1, 18, 1, 19}) @@ -571,28 +634,55 @@ int x = INT_MAX; eq(result, "value") end) - end) - - describe("when setting for a capture match", function() - it("should set/get the data correctly", function() - insert([[ - int x = 3; - ]]) - - local result = exec_lua([[ - local result - - query = vim.treesitter.parse_query("c", '((number_literal) @number (#set! @number "key" "value"))') - parser = vim.treesitter.get_parser(0, "c") - for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0) do - result = metadata[pattern].key - end - - return result - ]]) - - eq(result, "value") + describe("when setting a key on a capture", function() + it("it should create the nested table", function() + insert([[ + int x = 3; + ]]) + + local result = exec_lua([[ + local query = require("vim.treesitter.query") + local value + + query = vim.treesitter.parse_query("c", '((number_literal) @number (#set! @number "key" "value"))') + parser = vim.treesitter.get_parser(0, "c") + + for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0) do + for _, nested_tbl in pairs(metadata) do + return nested_tbl.key + end + end + ]]) + + eq(result, "value") + end) + + it("it should not overwrite the nested table", function() + insert([[ + int x = 3; + ]]) + + local result = exec_lua([[ + local query = require("vim.treesitter.query") + local result + + query = vim.treesitter.parse_query("c", '((number_literal) @number (#set! @number "key" "value") (#set! @number "key2" "value2"))') + parser = vim.treesitter.get_parser(0, "c") + + for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0) do + for _, nested_tbl in pairs(metadata) do + return nested_tbl + end + end + ]]) + local expected = { + ["key"] = "value", + ["key2"] = "value2", + } + + eq(expected, result) + end) end) end) end) diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index 21c01b3458..0ea8bab957 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen') local clear, feed = helpers.clear, helpers.feed local source = helpers.source local command = helpers.command +local assert_alive = helpers.assert_alive local function new_screen(opt) local screen = Screen.new(25, 5) @@ -758,6 +759,7 @@ local function test_cmdline(linegrid) end) it("doesn't send invalid events when aborting mapping #10000", function() + command('set notimeout') command('cnoremap ab c') feed(':xa') @@ -842,3 +844,14 @@ describe('cmdline redraw', function() ]], unchanged=true} end) end) + +describe("cmdline height", function() + it("does not crash resized screen #14263", function() + clear() + local screen = Screen.new(25, 10) + screen:attach() + command('set cmdheight=9999') + screen:try_resize(25, 5) + assert_alive() + end) +end) diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 295a54aec8..98aafd8757 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -29,6 +29,7 @@ describe('decorations providers', function() [10] = {italic = true, background = Screen.colors.Magenta}; [11] = {foreground = Screen.colors.Red, background = tonumber('0x005028')}; [12] = {foreground = tonumber('0x990000')}; + [13] = {background = Screen.colors.LightBlue}; } end) @@ -65,6 +66,18 @@ describe('decorations providers', function() expect_events(expected, actual, "beam trace") end + it('does not OOM when inserting, rather than appending, to the decoration provider vector', function() + -- Add a dummy decoration provider with a larger ns id than what setup_provider() creates. + -- This forces get_decor_provider() to insert into the providers vector, + -- rather than append, which used to spin in an infinite loop allocating + -- memory until nvim crashed/was killed. + setup_provider([[ + local ns2 = a.nvim_create_namespace "ns2" + a.nvim_set_decoration_provider(ns2, {}) + ]]) + helpers.assert_alive() + end) + it('leave a trace', function() insert(mulholland) @@ -331,10 +344,70 @@ describe('decorations providers', function() | ]]} end) + + it('can have virtual text of the style: right_align', function() + insert(mulholland) + setup_provider [[ + local hl = a.nvim_get_hl_id_by_name "ErrorMsg" + local test_ns = a.nvim_create_namespace "mulholland" + function on_do(event, ...) + if event == "line" then + local win, buf, line = ... + a.nvim_buf_set_extmark(buf, test_ns, line, 0, { + virt_text = {{'+'}, {string.rep(' ', line+1), 'ErrorMsg'}}; + virt_text_pos='right_align'; + ephemeral = true; + }) + end + end + ]] + + screen:expect{grid=[[ + // just to see if there was an acciden+{2: }| + // on Mulholland Drive +{2: }| + try_start(); +{2: }| + bufref_T save_buf; +{2: }| + switch_buffer(&save_buf, buf); +{2: }| + posp = getmark(mark, false); +{2: }| + restore_buffer(&save_buf);^ +{2: }| + | + ]]} + end) + + it('can highlight beyond EOL', function() + insert(mulholland) + setup_provider [[ + local test_ns = a.nvim_create_namespace "veberod" + function on_do(event, ...) + if event == "line" then + local win, buf, line = ... + if string.find(a.nvim_buf_get_lines(buf, line, line+1, true)[1], "buf") then + a.nvim_buf_set_extmark(buf, test_ns, line, 0, { + end_line = line+1; + hl_group = 'DiffAdd'; + hl_eol = true; + ephemeral = true; + }) + end + end + end + ]] + + screen:expect{grid=[[ + // just to see if there was an accident | + // on Mulholland Drive | + try_start(); | + {13:bufref_T save_buf; }| + {13:switch_buffer(&save_buf, buf); }| + posp = getmark(mark, false); | + {13:restore_buffer(&save_buf);^ }| + | + ]]} + end) end) describe('extmark decorations', function() - local screen + local screen, ns before_each( function() clear() screen = Screen.new(50, 15) @@ -365,6 +438,8 @@ describe('extmark decorations', function() [23] = {foreground = Screen.colors.Magenta1, background = Screen.colors.LightGrey}; [24] = {bold = true}; } + + ns = meths.create_namespace 'test' end) local example_text = [[ @@ -385,7 +460,6 @@ end]] insert(example_text) feed 'gg' - local ns = meths.create_namespace 'test' for i = 1,9 do meths.buf_set_extmark(0, ns, i, 0, { virt_text={{'|', 'LineNr'}}, virt_text_pos='overlay'}) if i == 3 or (i >= 6 and i <= 9) then @@ -452,7 +526,6 @@ end]] it('can have virtual text of overlay position and styling', function() insert(example_text) feed 'gg' - local ns = meths.create_namespace 'test' command 'set ft=lua' command 'syntax on' @@ -540,4 +613,88 @@ end]] {24:-- VISUAL LINE --} | ]]} end) + + it('can have virtual text of fixed win_col position', function() + insert(example_text) + feed 'gg' + meths.buf_set_extmark(0, ns, 1, 0, { virt_text={{'Very', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) + meths.buf_set_extmark(0, ns, 2, 10, { virt_text={{'Much', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) + meths.buf_set_extmark(0, ns, 3, 15, { virt_text={{'Error', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) + meths.buf_set_extmark(0, ns, 7, 21, { virt_text={{'-', 'NonText'}}, virt_text_win_col=4, hl_mode='blend'}) + + screen:expect{grid=[[ + ^for _,item in ipairs(items) do | + local text, hl_id_cell, cou{4:Very} unpack(item) | + if hl_id_cell ~= nil then {4:Much} | + hl_id = hl_id_cell {4:Error} | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + {1:-} cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + {1:~ }| + | + ]]} + + feed '3G12|i<cr><esc>' + screen:expect{grid=[[ + for _,item in ipairs(items) do | + local text, hl_id_cell, cou{4:Very} unpack(item) | + if hl_i {4:Much} | + ^d_cell ~= nil then | + hl_id = hl_id_cell {4:Error} | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + {1:-} cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + | + ]]} + + feed 'u:<cr>' + screen:expect{grid=[[ + for _,item in ipairs(items) do | + local text, hl_id_cell, cou{4:Very} unpack(item) | + if hl_i^d_cell ~= nil then {4:Much} | + hl_id = hl_id_cell {4:Error} | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + {1:-} cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + {1:~ }| + : | + ]]} + + feed '8|i<cr><esc>' + screen:expect{grid=[[ + for _,item in ipairs(items) do | + local text, hl_id_cell, cou{4:Very} unpack(item) | + if | + ^hl_id_cell ~= nil then {4:Much} | + hl_id = hl_id_cell {4:Error} | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + {1:-} cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + | + ]]} + end) end) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 664b8e7ab7..66aaf0c941 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -42,6 +42,10 @@ describe('float window', function() [20] = {bold = true, foreground = Screen.colors.Brown}, [21] = {background = Screen.colors.Gray90}, [22] = {background = Screen.colors.LightRed}, + [23] = {foreground = Screen.colors.Black, background = Screen.colors.White}; + [24] = {foreground = Screen.colors.Black, background = Screen.colors.Grey80}; + [25] = {blend = 100, background = Screen.colors.Gray0}; + [26] = {blend = 80, background = Screen.colors.Gray0}; } it('behavior', function() @@ -58,6 +62,24 @@ describe('float window', function() eq(1000, funcs.win_getid()) end) + it('win_execute() should work' , function() + local buf = meths.create_buf(false, false) + meths.buf_set_lines(buf, 0, -1, true, {'the floatwin'}) + local win = meths.open_win(buf, false, {relative='win', width=16, height=1, row=0, col=10}) + local line = funcs.win_execute(win, 'echo getline(1)') + eq('\nthe floatwin', line) + funcs.win_execute(win, 'bwipe!') + end) + + it('win_execute() call commands that not allowed' , function() + local buf = meths.create_buf(false, false) + meths.buf_set_lines(buf, 0, -1, true, {'the floatwin'}) + local win = meths.open_win(buf, true, {relative='win', width=16, height=1, row=0, col=10}) + eq(pcall_err(funcs.win_execute, win, 'close'), 'Vim(close):E37: No write since last change (add ! to override)') + eq(pcall_err(funcs.win_execute, win, 'bdelete'), 'Vim(bdelete):E89: No write since last change for buffer 2 (add ! to override)') + funcs.win_execute(win, 'bwipe!') + end) + it('closed immediately by autocmd #11383', function() eq('Error executing lua: [string "<nvim>"]:0: Window was closed immediately', pcall_err(exec_lua, [[ @@ -644,7 +666,6 @@ describe('float window', function() end meths.win_set_config(win, {border="single"}) - if multigrid then screen:expect{grid=[[ ## grid 1 @@ -687,9 +708,51 @@ describe('float window', function() ]]} end + meths.win_set_config(win, {border="solid"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 5 + {5: }| + {5: }{1: halloj! }{5: }| + {5: }{1: BORDAA }{5: }| + {5: }| + ]], float_pos={ + [5] = { { id = 1002 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5: }{0: }| + {0:~ }{5: }{1: halloj! }{5: }{0: }| + {0:~ }{5: }{1: BORDAA }{5: }{0: }| + {0:~ }{5: }{0: }| + | + ]]} + end + -- support: ascii char, UTF-8 char, composed char, highlight per char meths.win_set_config(win, {border={"x", {"å", "ErrorMsg"}, {"\\"}, {"n̈̊", "Search"}}}) - if multigrid then screen:expect{grid=[[ ## grid 1 @@ -710,10 +773,10 @@ describe('float window', function() ## grid 3 | ## grid 5 - {5:xååååååååå\}| - {5:n̈̊}{1: halloj! }{5:n̈̊}| - {5:n̈̊}{1: BORDAA }{5:n̈̊}| - {5:\åååååååååx}| + {5:x}{7:ååååååååå}{5:\}| + {17:n̈̊}{1: halloj! }{17:n̈̊}| + {17:n̈̊}{1: BORDAA }{17:n̈̊}| + {5:\}{7:ååååååååå}{5:x}| ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ @@ -772,6 +835,148 @@ describe('float window', function() | ]]} end + + meths.win_set_config(win, {border={"", "", "", ">", "", "", "", "<"}}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 5 + {5:<}{1: halloj! }{5:>}| + {5:<}{1: BORDAA }{5:>}| + ]], float_pos={ + [5] = { { id = 1002 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:<}{1: halloj! }{5:>}{0: }| + {0:~ }{5:<}{1: BORDAA }{5:>}{0: }| + {0:~ }| + {0:~ }| + | + ]]} + end + + insert [[ + neeed some dummy + background text + to show the effect + of color blending + of border shadow + ]] + + meths.win_set_config(win, {border="shadow"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + neeed some dummy | + background text | + to show the effect | + of color blending | + of border shadow | + ^ | + ## grid 3 + | + ## grid 5 + {1: halloj! }{25: }| + {1: BORDAA }{26: }| + {25: }{26: }| + ]], float_pos={ + [5] = { { id = 1002 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 6, curline = 5, curcol = 0}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + }} + else + screen:expect{grid=[[ + neeed some dummy | + background text | + to {1: halloj! }{23:e}ffect | + of {1: BORDAA }{24:n}ding | + of {23:b}{24:order sha}dow | + ^ | + | + ]]} + end + end) + + it('terminates border on edge of viewport when window extends past viewport', function() + local buf = meths.create_buf(false, false) + meths.open_win(buf, false, {relative='editor', width=40, height=7, row=0, col=0, border="single"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:┌────────────────────────────────────────┐}| + {5:│}{1: }{5:│}| + {5:│}{2:~ }{5:│}| + {5:│}{2:~ }{5:│}| + {5:│}{2:~ }{5:│}| + {5:│}{2:~ }{5:│}| + {5:│}{2:~ }{5:│}| + {5:│}{2:~ }{5:│}| + {5:└────────────────────────────────────────┘}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 0, 0, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; + }} + else + screen:expect{grid=[[ + {5:^┌──────────────────────────────────────┐}| + {5:│}{1: }{5:│}| + {5:│}{2:~ }{5:│}| + {5:│}{2:~ }{5:│}| + {5:│}{2:~ }{5:│}| + {5:└──────────────────────────────────────┘}| + | + ]]} + end end) it('with border show popupmenu', function() @@ -835,7 +1040,6 @@ describe('float window', function() end feed 'i<c-x><c-p>' - if multigrid then screen:expect{grid=[[ ## grid 1 @@ -873,12 +1077,8 @@ describe('float window', function() {1: abb }| {13: acc }| ]], float_pos={ - [5] = { { - id = 1002 - }, "NW", 1, 0, 5, true }, - [6] = { { - id = -1 - }, "NW", 5, 4, 0, false } + [5] = { { id = 1002 }, "NW", 1, 0, 5, true }, + [6] = { { id = -1 }, "NW", 5, 4, 0, false } }, win_viewport={ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 3}; @@ -5886,6 +6086,216 @@ describe('float window', function() ]]) end end) + + it("correctly orders multiple opened floats (current last)", function() + local buf = meths.create_buf(false,false) + local win = meths.open_win(buf, false, {relative='editor', width=20, height=2, row=2, col=5}) + meths.win_set_option(win, "winhl", "Normal:ErrorMsg,EndOfBuffer:ErrorMsg") + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {7: }| + {7:~ }| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true }; + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{7: }{0: }| + {0:~ }{7:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]} + end + + exec_lua [[ + local buf = vim.api.nvim_create_buf(false,false) + local win = vim.api.nvim_open_win(buf, false, {relative='editor', width=16, height=2, row=3, col=8}) + vim.api.nvim_win_set_option(win, "winhl", "EndOfBuffer:Normal") + buf = vim.api.nvim_create_buf(false,false) + win = vim.api.nvim_open_win(buf, true, {relative='editor', width=12, height=2, row=4, col=10}) + vim.api.nvim_win_set_option(win, "winhl", "Normal:Search,EndOfBuffer:Search") + ]] + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {7: }| + {7:~ }| + ## grid 5 + {1: }| + {1:~ }| + ## grid 6 + {17:^ }| + {17:~ }| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true }; + [5] = { { id = 1002 }, "NW", 1, 3, 8, true }; + [6] = { { id = 1003 }, "NW", 1, 4, 10, true }; + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0}; + }} + else + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }{7: }{0: }| + {0:~ }{7:~ }{1: }{7: }{0: }| + {0:~ }{1:~ }{17:^ }{1: }{0: }| + {0:~ }{17:~ }{0: }| + | + ]]} + end + end) + + it("correctly orders multiple opened floats (non-current last)", function() + local buf = meths.create_buf(false,false) + local win = meths.open_win(buf, false, {relative='editor', width=20, height=2, row=2, col=5}) + meths.win_set_option(win, "winhl", "Normal:ErrorMsg,EndOfBuffer:ErrorMsg") + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {7: }| + {7:~ }| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true }; + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{7: }{0: }| + {0:~ }{7:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]} + end + + exec_lua [[ + local buf = vim.api.nvim_create_buf(false,false) + local win = vim.api.nvim_open_win(buf, true, {relative='editor', width=12, height=2, row=4, col=10}) + vim.api.nvim_win_set_option(win, "winhl", "Normal:Search,EndOfBuffer:Search") + buf = vim.api.nvim_create_buf(false,false) + win = vim.api.nvim_open_win(buf, false, {relative='editor', width=16, height=2, row=3, col=8}) + vim.api.nvim_win_set_option(win, "winhl", "EndOfBuffer:Normal") + ]] + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {7: }| + {7:~ }| + ## grid 5 + {17:^ }| + {17:~ }| + ## grid 6 + {1: }| + {1:~ }| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true }; + [5] = { { id = 1002 }, "NW", 1, 4, 10, true }; + [6] = { { id = 1003 }, "NW", 1, 3, 8, true }; + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0}; + }} + else + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }{7: }{0: }| + {0:~ }{7:~ }{1: }{7: }{0: }| + {0:~ }{1:~ }{17:^ }{1: }{0: }| + {0:~ }{17:~ }{0: }| + | + ]]} + end + end) end describe('with ext_multigrid', function() diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 1fe3a4128e..9d7719a7c0 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -456,6 +456,8 @@ describe('ui/ext_messages', function() {1:~ }| ]], messages={ {kind="echomsg", content={{"stuff"}}}, + }, showmode={ + { "-- INSERT --", 3 } }} end) diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index 656f613c6a..5540b3c2dc 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -507,7 +507,21 @@ describe('search highlighting', function() {1:~ }| :syntax keyword MyGroup special | ]]) + end) + it('highlights entire pattern on :%g@a/b', function() + command('set inccommand=nosplit') + feed('ia/b/c<Esc>') + feed(':%g@a/b') + screen:expect([[ + {3:a/b}/c | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :%g@a/b^ | + ]]) end) end) diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua index 1937102782..06c92a4b10 100644 --- a/test/functional/ui/sign_spec.lua +++ b/test/functional/ui/sign_spec.lua @@ -264,6 +264,24 @@ describe('Signs', function() {0:~ }| | ]]} + -- line deletion deletes signs. + command('2d') + screen:expect([[ + {1:>>}XX{2: }{6: 1 }a | + XX{1:>>}WW{6: 2 }^c | + {2: }{6: 3 } | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) end) it('auto-resize sign column with minimum size (#13783)', function() diff --git a/test/functional/viml/errorlist_spec.lua b/test/functional/viml/errorlist_spec.lua index 9acc61e398..077d816903 100644 --- a/test/functional/viml/errorlist_spec.lua +++ b/test/functional/viml/errorlist_spec.lua @@ -68,4 +68,17 @@ describe('setloclist()', function() command('lclose | wincmd w | lopen') eq('foo', get_cur_win_var('quickfix_title')) end) + + it("doesn't crash when when window is closed in the middle #13721", function() + helpers.insert([[ + hello world]]) + + command("vsplit") + command("autocmd WinLeave * :call nvim_win_close(0, v:true)") + + command("call setloclist(0, [])") + command("lopen") + + helpers.assert_alive() + end) end) diff --git a/test/helpers.lua b/test/helpers.lua index 8dbd82cb8c..12d9f19187 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -365,7 +365,11 @@ function module.check_cores(app, force) db_cmd = lldb_db_cmd else initial_path = '.' - re = '/core[^/]*$' + if 'freebsd' == module.uname() then + re = '/nvim.core$' + else + re = '/core[^/]*$' + end exc_re = { '^/%.deps$', '^/%'..deps_prefix()..'$', local_tmpdir, '^/%node_modules$' } db_cmd = gdb_db_cmd random_skip = true |