diff options
Diffstat (limited to 'test')
29 files changed, 1628 insertions, 116 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 77fe7b3b82..e9be79edc0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,7 +27,7 @@ if(LUA_HAS_FFI) -P ${PROJECT_SOURCE_DIR}/cmake/RunTests.cmake DEPENDS ${UNITTEST_PREREQS} USES_TERMINAL) - add_dependencies(unittest test_deps) + add_dependencies(unittest lua-dev-deps) else() message(WARNING "disabling unit tests: no Luajit FFI in ${LUA_PRG}") endif() @@ -66,5 +66,5 @@ add_custom_target(benchmark DEPENDS ${BENCHMARK_PREREQS} USES_TERMINAL) -add_dependencies(functionaltest test_deps) -add_dependencies(benchmark test_deps) +add_dependencies(functionaltest lua-dev-deps) +add_dependencies(benchmark lua-dev-deps) diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 292e5a2d56..9833ebee4c 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -848,6 +848,710 @@ describe('api/buf', function() eq({1, 4}, meths.win_get_cursor(win2)) end) + describe('when text is being added right at cursor position #22526', function() + it('updates the cursor position in NORMAL mode', function() + insert([[ + abcd]]) + + -- position the cursor on 'c' + curwin('set_cursor', {1, 2}) + -- add 'xxx' before 'c' + set_text(0, 2, 0, 2, {'xxx'}) + eq({'abxxxcd'}, get_lines(0, -1, true)) + -- cursor should be on 'c' + eq({1, 5}, curwin('get_cursor')) + end) + + it('updates the cursor position only in non-current window when in INSERT mode', function() + insert([[ + abcd]]) + + -- position the cursor on 'c' + curwin('set_cursor', {1, 2}) + -- open vertical split + feed('<c-w>v') + -- get into INSERT mode to treat cursor + -- as being after 'b', not on 'c' + feed('i') + -- add 'xxx' between 'b' and 'c' + set_text(0, 2, 0, 2, {'xxx'}) + eq({'abxxxcd'}, get_lines(0, -1, true)) + -- in the current window cursor should stay after 'b' + eq({1, 2}, curwin('get_cursor')) + -- quit INSERT mode + feed('<esc>') + -- close current window + feed('<c-w>c') + -- in another window cursor should be on 'c' + eq({1, 5}, curwin('get_cursor')) + end) + end) + + describe('when text is being deleted right at cursor position', function() + it('leaves cursor at the same position in NORMAL mode', function() + insert([[ + abcd]]) + + -- position the cursor on 'b' + curwin('set_cursor', {1, 1}) + -- delete 'b' + set_text(0, 1, 0, 2, {}) + eq({'acd'}, get_lines(0, -1, true)) + -- cursor is now on 'c' + eq({1, 1}, curwin('get_cursor')) + end) + + it('leaves cursor at the same position in INSERT mode in current and non-current window', function() + insert([[ + abcd]]) + + -- position the cursor on 'b' + curwin('set_cursor', {1, 1}) + -- open vertical split + feed('<c-w>v') + -- get into INSERT mode to treat cursor + -- as being after 'a', not on 'b' + feed('i') + -- delete 'b' + set_text(0, 1, 0, 2, {}) + eq({'acd'}, get_lines(0, -1, true)) + -- cursor in the current window should stay after 'a' + eq({1, 1}, curwin('get_cursor')) + -- quit INSERT mode + feed('<esc>') + -- close current window + feed('<c-w>c') + -- cursor in non-current window should stay on 'c' + eq({1, 1}, curwin('get_cursor')) + end) + end) + + describe('when cursor is inside replaced row range', function() + it('keeps cursor at the same position if cursor is at start_row, but before start_col', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on ' ' before 'first' + curwin('set_cursor', {1, 14}) + + set_text(0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should stay at the same position + eq({1, 14}, curwin('get_cursor')) + end) + + it('keeps cursor at the same position if cursor is at start_row and column is still valid', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'f' in 'first' + curwin('set_cursor', {1, 15}) + + set_text(0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should stay at the same position + eq({1, 15}, curwin('get_cursor')) + end) + + it('adjusts cursor column to keep it valid if start_row got smaller', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 't' in 'first' + curwin('set_cursor', {1, 19}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 24, {'last'}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ 'This should be last' }, get_lines(0, -1, true)) + -- cursor should end up on 't' in 'last' + eq({1, 18}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 18}, cursor) + end) + + it('adjusts cursor column to keep it valid if start_row got smaller in INSERT mode', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 't' in 'first' + curwin('set_cursor', {1, 19}) + -- enter INSERT mode to treat cursor as being after 't' + feed('a') + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 24, {'last'}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ 'This should be last' }, get_lines(0, -1, true)) + -- cursor should end up after 't' in 'last' + eq({1, 19}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 19}, cursor) + end) + + it('adjusts cursor column to keep it valid in a row after start_row if it got smaller', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'w' in 'want' + curwin('set_cursor', {2, 31}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { + '1', + 'then 2', + 'and then', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be 1', + 'then 2', + 'and then the last one', + }, get_lines(0, -1, true)) + -- cursor column should end up at the end of a row + eq({2, 5}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({2, 5}, cursor) + end) + + it('adjusts cursor column to keep it valid in a row after start_row if it got smaller in INSERT mode', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'w' in 'want' + curwin('set_cursor', {2, 31}) + -- enter INSERT mode + feed('a') + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { + '1', + 'then 2', + 'and then', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be 1', + 'then 2', + 'and then the last one', + }, get_lines(0, -1, true)) + -- cursor column should end up at the end of a row + eq({2, 6}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({2, 6}, cursor) + end) + + it('adjusts cursor line and column to keep it inside replacement range', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'n' in 'finally' + curwin('set_cursor', {3, 6}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should end up on 'y' in 'hopefully' + -- to stay in the range, because it got smaller + eq({2, 12}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({2, 12}, cursor) + end) + + it('adjusts cursor line and column if replacement is empty', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'r' in 'there' + curwin('set_cursor', {2, 8}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 12, {}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ 'This should be the last one' }, get_lines(0, -1, true)) + -- cursor should end up on the next column after deleted range + eq({1, 15}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 15}, cursor) + end) + + it('adjusts cursor line and column if replacement is empty and start_col == 0', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'r' in 'there' + curwin('set_cursor', {2, 8}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 0, 2, 4, {}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ 'finally the last one' }, get_lines(0, -1, true)) + -- cursor should end up in column 0 + eq({1, 0}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 0}, cursor) + end) + + it('adjusts cursor column if replacement ends at cursor row, after cursor column', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'y' in 'finally' + curwin('set_cursor', {3, 10}) + set_text(0, 15, 2, 11, { '1', 'this 2', 'and then' }) + + eq({ + 'This should be 1', + 'this 2', + 'and then the last one', + }, get_lines(0, -1, true)) + -- cursor should end up on 'n' in 'then' + eq({3, 7}, curwin('get_cursor')) + end) + + it('adjusts cursor column if replacement ends at cursor row, at cursor column in INSERT mode', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'y' at 'finally' + curwin('set_cursor', {3, 10}) + -- enter INSERT mode to treat cursor as being between 'l' and 'y' + feed('i') + set_text(0, 15, 2, 11, { '1', 'this 2', 'and then' }) + + eq({ + 'This should be 1', + 'this 2', + 'and then the last one', + }, get_lines(0, -1, true)) + -- cursor should end up after 'n' in 'then' + eq({3, 8}, curwin('get_cursor')) + end) + + it('adjusts cursor column if replacement is inside of a single line', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'y' in 'finally' + curwin('set_cursor', {3, 10}) + set_text(2, 4, 2, 11, { 'then' }) + + eq({ + 'This should be first', + 'then there is a line we do not want', + 'and then the last one', + }, get_lines(0, -1, true)) + -- cursor should end up on 'n' in 'then' + eq({3, 7}, curwin('get_cursor')) + end) + + it('does not move cursor column after end of a line', function() + insert([[ + This should be the only line here + !!!]]) + + -- position cursor on the last '1' + curwin('set_cursor', {2, 2}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 33, 1, 3, {}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ 'This should be the only line here' }, get_lines(0, -1, true)) + -- cursor should end up on '!' + eq({1, 32}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 32}, cursor) + end) + + it('does not move cursor column before start of a line', function() + insert('\n!!!') + + -- position cursor on the last '1' + curwin('set_cursor', {2, 2}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 0, 1, 3, {}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ '' }, get_lines(0, -1, true)) + -- cursor should end up on '!' + eq({1, 0}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 0}, cursor) + end) + + describe('with virtualedit', function() + it('adjusts cursor line and column to keep it inside replacement range if cursor is not after eol', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position cursor on 't' in 'want' + curwin('set_cursor', {2, 34}) + -- turn on virtualedit + command('set virtualedit=all') + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should end up on 'y' in 'hopefully' + -- to stay in the range + eq({2, 12}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({2, 12}, cursor) + -- coladd should be 0 + eq(0, exec_lua([[ + return vim.fn.winsaveview().coladd + ]])) + end) + + it('does not change cursor screen column when cursor is after eol and row got shorter', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position cursor on 't' in 'want' + curwin('set_cursor', {2, 34}) + -- turn on virtualedit + command('set virtualedit=all') + -- move cursor after eol + exec_lua([[ + vim.fn.winrestview({ coladd = 5 }) + ]]) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should end up at eol of a new row + eq({2, 26}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({2, 26}, cursor) + -- coladd should be increased so that cursor stays in the same screen column + eq(13, exec_lua([[ + return vim.fn.winsaveview().coladd + ]])) + end) + + it('does not change cursor screen column when cursor is after eol and row got longer', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position cursor on 't' in 'first' + curwin('set_cursor', {1, 19}) + -- turn on virtualedit + command('set virtualedit=all') + -- move cursor after eol + exec_lua([[ + vim.fn.winrestview({ coladd = 21 }) + ]]) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should end up at eol of a new row + eq({1, 38}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 38}, cursor) + -- coladd should be increased so that cursor stays in the same screen column + eq(2, exec_lua([[ + return vim.fn.winsaveview().coladd + ]])) + end) + + it('does not change cursor screen column when cursor is after eol and row extended past cursor column', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position cursor on 't' in 'first' + curwin('set_cursor', {1, 19}) + -- turn on virtualedit + command('set virtualedit=all') + -- move cursor after eol just a bit + exec_lua([[ + vim.fn.winrestview({ coladd = 3 }) + ]]) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should stay at the same screen column + eq({1, 22}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 22}, cursor) + -- coladd should become 0 + eq(0, exec_lua([[ + return vim.fn.winsaveview().coladd + ]])) + end) + + it('does not change cursor screen column when cursor is after eol and row range decreased', function() + insert([[ + This should be first + then there is a line we do not want + and one more + and finally the last one]]) + + -- position cursor on 'e' in 'more' + curwin('set_cursor', {3, 11}) + -- turn on virtualedit + command('set virtualedit=all') + -- move cursor after eol + exec_lua([[ + vim.fn.winrestview({ coladd = 28 }) + ]]) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 3, 11, { + 'the line we do not want', + 'but hopefully', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should end up at eol of a new row + eq({2, 26}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({2, 26}, cursor) + -- coladd should be increased so that cursor stays in the same screen column + eq(13, exec_lua([[ + return vim.fn.winsaveview().coladd + ]])) + end) + end) + end) + + describe('when cursor is at end_row and after end_col', function() + it('adjusts cursor column when only a newline is added or deleted', function() + insert([[ + first line + second + line]]) + + -- position the cursor on 'i' + curwin('set_cursor', {3, 2}) + set_text(1, 6, 2, 0, {}) + eq({'first line', 'second line'}, get_lines(0, -1, true)) + -- cursor should stay on 'i' + eq({2, 8}, curwin('get_cursor')) + + -- add a newline back + set_text(1, 6, 1, 6, {'', ''}) + eq({'first line', 'second', ' line'}, get_lines(0, -1, true)) + -- cursor should return back to the original position + eq({3, 2}, curwin('get_cursor')) + end) + + it('adjusts cursor column if the range is not bound to either start or end of a line', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'h' in 'the' + curwin('set_cursor', {3, 13}) + set_text(0, 14, 2, 11, {}) + eq({'This should be the last one'}, get_lines(0, -1, true)) + -- cursor should stay on 'h' + eq({1, 16}, curwin('get_cursor')) + -- add deleted lines back + set_text(0, 14, 0, 14, { + ' first', + 'then there is a line we do not want', + 'and finally', + }) + eq({ + 'This should be first', + 'then there is a line we do not want', + 'and finally the last one', + }, get_lines(0, -1, true)) + -- cursor should return back to the original position + eq({3, 13}, curwin('get_cursor')) + end) + + it('adjusts cursor column if replacing lines in range, not just deleting and adding', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 's' in 'last' + curwin('set_cursor', {3, 18}) + set_text(0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should stay on 's' + eq({2, 20}, curwin('get_cursor')) + + set_text(0, 15, 1, 13, { + 'first', + 'then there is a line we do not want', + 'and finally', + }) + + eq({ + 'This should be first', + 'then there is a line we do not want', + 'and finally the last one', + }, get_lines(0, -1, true)) + -- cursor should return back to the original position + eq({3, 18}, curwin('get_cursor')) + end) + + it('does not move cursor column after end of a line', function() + insert([[ + This should be the only line here + ]]) + + -- position cursor at the empty line + curwin('set_cursor', {2, 0}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 33, 1, 0, {'!'}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ 'This should be the only line here!' }, get_lines(0, -1, true)) + -- cursor should end up on '!' + eq({1, 33}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 33}, cursor) + end) + + it('does not move cursor column before start of a line', function() + insert('\n') + + eq({ '', '' }, get_lines(0, -1, true)) + + -- position cursor on the last '1' + curwin('set_cursor', {2, 2}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 0, 1, 0, {''}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ '' }, get_lines(0, -1, true)) + -- cursor should end up on '!' + eq({1, 0}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 0}, cursor) + end) + end) + it('can handle NULs', function() set_text(0, 0, 0, 0, {'ab\0cd'}) eq('ab\0cd', curbuf_depr('get_line', 0)) diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index 6d8e3d8e0a..a917432dab 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -753,7 +753,14 @@ describe('API/extmarks', function() }) end) - -- TODO(bfredl): add more tests! + it('can get overlapping extmarks', function() + set_extmark(ns, 1, 0, 0, {end_row = 5, end_col=0}) + set_extmark(ns, 2, 2, 5, {end_row = 2, end_col=30}) + set_extmark(ns, 3, 0, 5, {end_row = 2, end_col=10}) + set_extmark(ns, 4, 0, 0, {end_row = 1, end_col=0}) + eq({{ 2, 2, 5 }}, get_extmarks(ns, {2, 0}, {2, -1}, { overlap=false })) + eq({{ 1, 0, 0 }, { 3, 0, 5}, {2, 2, 5}}, get_extmarks(ns, {2, 0}, {2, -1}, { overlap=true })) + end) end) it('replace works', function() diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index 5fa2235018..492fd73223 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -439,6 +439,15 @@ describe('API: get highlight', function() eq('Highlight id out of bounds', pcall_err(meths.get_hl, 0, { name = 'Test set hl' })) end) + it('nvim_get_hl with create flag', function() + eq({}, nvim("get_hl", 0, {name = 'Foo', create = false})) + eq(0, funcs.hlexists('Foo')) + meths.get_hl(0, {name = 'Bar', create = true}) + eq(1, funcs.hlexists('Bar')) + meths.get_hl(0, {name = 'FooBar'}) + eq(1, funcs.hlexists('FooBar')) + end) + it('can get all highlights in current namespace', function() local ns = get_ns() meths.set_hl(ns, 'Test_hl', { bg = '#B4BEFE' }) diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua index 53642858b2..bc43f6564d 100644 --- a/test/functional/api/server_notifications_spec.lua +++ b/test/functional/api/server_notifications_spec.lua @@ -6,9 +6,7 @@ local eq, clear, eval, command, nvim, next_msg = local meths = helpers.meths local exec_lua = helpers.exec_lua local retry = helpers.retry -local is_ci = helpers.is_ci local assert_alive = helpers.assert_alive -local skip = helpers.skip local testlog = 'Xtest-server-notify-log' @@ -90,7 +88,6 @@ describe('notify', function() end) it('cancels stale events on channel close', function() - skip(is_ci(), 'hangs on CI #14083 #15251') local catchan = eval("jobstart(['cat'], {'rpc': v:true})") local catpath = eval('exepath("cat")') eq({id=catchan, argv={catpath}, stream='job', mode='rpc', client = {}}, exec_lua ([[ diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index b98cf97e7e..6f5397a089 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -857,6 +857,11 @@ function module.testprg(name) return ('%s/%s%s'):format(module.nvim_dir, name, ext) end +function module.is_asan() + local version = module.eval('execute("verbose version")') + return version:match('-fsanitize=[a-z,]*address') +end + -- Returns a valid, platform-independent Nvim listen address. -- Useful for communicating with child instances. function module.new_pipename() diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua index bf37315914..5f722e5190 100644 --- a/test/functional/legacy/memory_usage_spec.lua +++ b/test/functional/legacy/memory_usage_spec.lua @@ -11,14 +11,10 @@ local load_adjust = helpers.load_adjust local write_file = helpers.write_file local is_os = helpers.is_os local is_ci = helpers.is_ci - -local function isasan() - local version = eval('execute("verbose version")') - return version:match('-fsanitize=[a-z,]*address') -end +local is_asan = helpers.is_asan clear() -if isasan() then +if is_asan() then pending('ASAN build is difficult to estimate memory usage', function() end) return elseif is_os('win') then diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index c19891a794..51e4548edb 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -826,53 +826,53 @@ describe('lua: nvim_buf_attach on_bytes', function() feed("<esc>u") check_events { - { "test1", "bytes", 1, 8, 0, 0, 0, 0, 1, 1, 0, 4, 4 }, - { "test1", "bytes", 1, 8, 0, 0, 0, 0, 4, 4, 0, 0, 0 } + { "test1", "bytes", 1, 9, 0, 0, 0, 0, 1, 1, 0, 4, 4 }, + { "test1", "bytes", 1, 9, 0, 0, 0, 0, 4, 4, 0, 0, 0 } } -- in REPLACE mode feed("R<tab><tab>") check_events { - { "test1", "bytes", 1, 9, 0, 0, 0, 0, 1, 1, 0, 1, 1 }, - { "test1", "bytes", 1, 10, 0, 1, 1, 0, 0, 0, 0, 1, 1 }, - { "test1", "bytes", 1, 11, 0, 2, 2, 0, 1, 1, 0, 1, 1 }, - { "test1", "bytes", 1, 12, 0, 3, 3, 0, 0, 0, 0, 1, 1 }, - { "test1", "bytes", 1, 13, 0, 0, 0, 0, 4, 4, 0, 1, 1 }, + { "test1", "bytes", 1, 10, 0, 0, 0, 0, 1, 1, 0, 1, 1 }, + { "test1", "bytes", 1, 11, 0, 1, 1, 0, 0, 0, 0, 1, 1 }, + { "test1", "bytes", 1, 12, 0, 2, 2, 0, 1, 1, 0, 1, 1 }, + { "test1", "bytes", 1, 13, 0, 3, 3, 0, 0, 0, 0, 1, 1 }, + { "test1", "bytes", 1, 14, 0, 0, 0, 0, 4, 4, 0, 1, 1 }, } feed("<esc>u") check_events { - { "test1", "bytes", 1, 14, 0, 0, 0, 0, 1, 1, 0, 4, 4 }, - { "test1", "bytes", 1, 14, 0, 2, 2, 0, 2, 2, 0, 1, 1 }, - { "test1", "bytes", 1, 14, 0, 0, 0, 0, 2, 2, 0, 1, 1 } + { "test1", "bytes", 1, 16, 0, 0, 0, 0, 1, 1, 0, 4, 4 }, + { "test1", "bytes", 1, 16, 0, 2, 2, 0, 2, 2, 0, 1, 1 }, + { "test1", "bytes", 1, 16, 0, 0, 0, 0, 2, 2, 0, 1, 1 } } -- in VISUALREPLACE mode feed("gR<tab><tab>") check_events { - { "test1", "bytes", 1, 15, 0, 0, 0, 0, 1, 1, 0, 1, 1 }; - { "test1", "bytes", 1, 16, 0, 1, 1, 0, 1, 1, 0, 1, 1 }; - { "test1", "bytes", 1, 17, 0, 2, 2, 0, 1, 1, 0, 1, 1 }; - { "test1", "bytes", 1, 18, 0, 3, 3, 0, 1, 1, 0, 1, 1 }; - { "test1", "bytes", 1, 19, 0, 3, 3, 0, 1, 1, 0, 0, 0 }; - { "test1", "bytes", 1, 20, 0, 3, 3, 0, 0, 0, 0, 1, 1 }; - { "test1", "bytes", 1, 22, 0, 2, 2, 0, 1, 1, 0, 0, 0 }; - { "test1", "bytes", 1, 23, 0, 2, 2, 0, 0, 0, 0, 1, 1 }; - { "test1", "bytes", 1, 25, 0, 1, 1, 0, 1, 1, 0, 0, 0 }; - { "test1", "bytes", 1, 26, 0, 1, 1, 0, 0, 0, 0, 1, 1 }; - { "test1", "bytes", 1, 28, 0, 0, 0, 0, 1, 1, 0, 0, 0 }; - { "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 }; + { "test1", "bytes", 1, 17, 0, 0, 0, 0, 1, 1, 0, 1, 1 }; + { "test1", "bytes", 1, 18, 0, 1, 1, 0, 1, 1, 0, 1, 1 }; + { "test1", "bytes", 1, 19, 0, 2, 2, 0, 1, 1, 0, 1, 1 }; + { "test1", "bytes", 1, 20, 0, 3, 3, 0, 1, 1, 0, 1, 1 }; + { "test1", "bytes", 1, 21, 0, 3, 3, 0, 1, 1, 0, 0, 0 }; + { "test1", "bytes", 1, 22, 0, 3, 3, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 24, 0, 2, 2, 0, 1, 1, 0, 0, 0 }; + { "test1", "bytes", 1, 25, 0, 2, 2, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 27, 0, 1, 1, 0, 1, 1, 0, 0, 0 }; + { "test1", "bytes", 1, 28, 0, 1, 1, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 30, 0, 0, 0, 0, 1, 1, 0, 0, 0 }; + { "test1", "bytes", 1, 31, 0, 0, 0, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 33, 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 }; + { "test1", "bytes", 1, 34, 0, 1, 1, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 35, 0, 2, 2, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 36, 0, 3, 3, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 37, 0, 4, 4, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 38, 0, 1, 1, 0, 4, 4, 0, 1, 1 }; } end) diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua index 2c7b3ff324..6bdb9ed79d 100644 --- a/test/functional/lua/fs_spec.lua +++ b/test/functional/lua/fs_spec.lua @@ -1,4 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) +local uv = require('luv') local clear = helpers.clear local exec_lua = helpers.exec_lua @@ -288,11 +289,12 @@ describe('vim.fs', function() eq('/', exec_lua [[ return vim.fs.normalize('/') ]]) end) it('works with ~', function() - eq( exec_lua([[ - local home = ... - return home .. '/src/foo' - ]], is_os('win') and vim.fs.normalize(os.getenv('USERPROFILE')) or os.getenv('HOME') - ) , exec_lua [[ return vim.fs.normalize('~/src/foo') ]]) + eq( + exec_lua([[ + local home = ... + return home .. '/src/foo' + ]], vim.fs.normalize(uv.os_homedir())), + exec_lua [[ return vim.fs.normalize('~/src/foo') ]]) end) it('works with environment variables', function() local xdg_config_home = test_build_dir .. '/.config' diff --git a/test/functional/plugin/lsp/utils_spec.lua b/test/functional/plugin/lsp/utils_spec.lua index c91fffa90f..f544255d81 100644 --- a/test/functional/plugin/lsp/utils_spec.lua +++ b/test/functional/plugin/lsp/utils_spec.lua @@ -1,4 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local feed = helpers.feed local eq = helpers.eq local exec_lua = helpers.exec_lua @@ -85,4 +87,98 @@ describe('vim.lsp.util', function() eq(expected, stylize_markdown(lines, opts)) end) end) + + describe("make_floating_popup_options", function () + + local function assert_anchor(anchor_bias, expected_anchor) + local opts = exec_lua([[ + local args = { ... } + local anchor_bias = args[1] + return vim.lsp.util.make_floating_popup_options(30, 10, { anchor_bias = anchor_bias }) + ]], anchor_bias) + + eq(expected_anchor, string.sub(opts.anchor, 1, 1)) + end + + local screen + before_each(function () + helpers.clear() + screen = Screen.new(80, 80) + screen:attach() + feed("79i<CR><Esc>") -- fill screen with empty lines + end) + + describe('when on the first line it places window below', function () + before_each(function () + feed('gg') + end) + + it('for anchor_bias = "auto"', function () + assert_anchor('auto', 'N') + end) + + it('for anchor_bias = "above"', function () + assert_anchor('above', 'N') + end) + + it('for anchor_bias = "below"', function () + assert_anchor('below', 'N') + end) + end) + + describe('when on the last line it places window above', function () + before_each(function () + feed('G') + end) + + it('for anchor_bias = "auto"', function () + assert_anchor('auto', 'S') + end) + + it('for anchor_bias = "above"', function () + assert_anchor('above', 'S') + end) + + it('for anchor_bias = "below"', function () + assert_anchor('below', 'S') + end) + end) + + describe('with 20 lines above, 59 lines below', function () + before_each(function () + feed('gg20j') + end) + + it('places window below for anchor_bias = "auto"', function () + assert_anchor('auto', 'N') + end) + + it('places window above for anchor_bias = "above"', function () + assert_anchor('above', 'S') + end) + + it('places window below for anchor_bias = "below"', function () + assert_anchor('below', 'N') + end) + end) + + describe('with 59 lines above, 20 lines below', function () + before_each(function () + feed('G20k') + end) + + it('places window above for anchor_bias = "auto"', function () + assert_anchor('auto', 'S') + end) + + it('places window above for anchor_bias = "above"', function () + assert_anchor('above', 'S') + end) + + it('places window below for anchor_bias = "below"', function () + assert_anchor('below', 'N') + end) + end) + end) + end) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 3eb89b4556..e0a8badb67 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -1787,7 +1787,7 @@ describe('LSP', function() eq({ 'First line of text'; }, buf_lines(1)) - eq({ 1, 6 }, funcs.nvim_win_get_cursor(0)) + eq({ 1, 17 }, funcs.nvim_win_get_cursor(0)) end) it('fix the cursor row', function() diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua index db5dba7374..29361bc0fa 100644 --- a/test/functional/terminal/edit_spec.lua +++ b/test/functional/terminal/edit_spec.lua @@ -36,7 +36,6 @@ describe(':edit term://*', function() end) it("runs TermOpen early enough to set buffer-local 'scrollback'", function() - if helpers.skip(helpers.is_os('win')) then return end local columns, lines = 20, 4 local scr = get_screen(columns, lines) local rep = 97 diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 62dbd57202..daa4b4bdb3 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -103,7 +103,7 @@ describe('decorations providers', function() ]]} check_trace { { "start", 4 }; - { "win", 1000, 1, 0, 8 }; + { "win", 1000, 1, 0, 6 }; { "line", 1000, 1, 0 }; { "line", 1000, 1, 1 }; { "line", 1000, 1, 2 }; @@ -128,7 +128,7 @@ describe('decorations providers', function() check_trace { { "start", 5 }; { "buf", 1, 5 }; - { "win", 1000, 1, 0, 8 }; + { "win", 1000, 1, 0, 6 }; { "line", 1000, 1, 6 }; { "end", 5 }; } @@ -195,7 +195,7 @@ describe('decorations providers', function() check_trace { { "start", 5 }; - { "win", 1000, 1, 0, 5 }; + { "win", 1000, 1, 0, 3 }; { "line", 1000, 1, 0 }; { "line", 1000, 1, 1 }; { "line", 1000, 1, 2 }; @@ -691,6 +691,12 @@ describe('extmark decorations', function() [33] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray}; [34] = {background = Screen.colors.Yellow}; [35] = {background = Screen.colors.Yellow, bold = true, foreground = Screen.colors.Blue}; + [36] = {foreground = Screen.colors.Blue1, bold = true, background = Screen.colors.Red}; + [37] = {background = Screen.colors.WebGray, foreground = Screen.colors.DarkBlue}; + [38] = {background = Screen.colors.LightBlue}; + [39] = {foreground = Screen.colors.Blue1, background = Screen.colors.LightCyan1, bold = true}; + [40] = {reverse = true}; + [41] = {bold = true, reverse = true}; } ns = meths.create_namespace 'test' @@ -858,6 +864,104 @@ describe('extmark decorations', function() ]]} end) + it('overlay virtual text works with wrapped lines #25158', function() + screen:try_resize(50, 6) + insert(('ab'):rep(100)) + for i = 0, 9 do + meths.buf_set_extmark(0, ns, 0, 42 + i, { virt_text={{tostring(i), 'ErrorMsg'}}, virt_text_pos='overlay'}) + meths.buf_set_extmark(0, ns, 0, 91 + i, { virt_text={{tostring(i), 'ErrorMsg'}}, virt_text_pos='overlay', virt_text_hide=true}) + end + screen:expect{grid=[[ + ababababababababababababababababababababab{4:01234567}| + {4:89}abababababababababababababababababababa{4:012345678}| + {4:9}babababababababababababababababababababababababab| + ababababababababababababababababababababababababa^b| + {1:~ }| + | + ]]} + + command('set showbreak=++') + screen:expect{grid=[[ + ababababababababababababababababababababab{4:01234567}| + {1:++}{4:89}abababababababababababababababababababa{4:0123456}| + {1:++}{4:789}babababababababababababababababababababababab| + {1:++}abababababababababababababababababababababababab| + {1:++}ababa^b | + | + ]]} + + feed('2gkvg0') + screen:expect{grid=[[ + ababababababababababababababababababababab{4:01234567}| + {1:++}{4:89}abababababababababababababababababababa{4:0123456}| + {1:++}^a{18:babab}ababababababababababababababababababababab| + {1:++}abababababababababababababababababababababababab| + {1:++}ababab | + {24:-- VISUAL --} | + ]]} + + feed('o') + screen:expect{grid=[[ + ababababababababababababababababababababab{4:01234567}| + {1:++}{4:89}abababababababababababababababababababa{4:0123456}| + {1:++}{18:ababa}^bababababababababababababababababababababab| + {1:++}abababababababababababababababababababababababab| + {1:++}ababab | + {24:-- VISUAL --} | + ]]} + + feed('gk') + screen:expect{grid=[[ + ababababababababababababababababababababab{4:01234567}| + {1:++}{4:89}aba^b{18:ababababababababababababababababababababab}| + {1:++}{18:a}{4:89}babababababababababababababababababababababab| + {1:++}abababababababababababababababababababababababab| + {1:++}ababab | + {24:-- VISUAL --} | + ]]} + + feed('o') + screen:expect{grid=[[ + ababababababababababababababababababababab{4:01234567}| + {1:++}{4:89}aba{18:bababababababababababababababababababababab}| + {1:++}^a{4:89}babababababababababababababababababababababab| + {1:++}abababababababababababababababababababababababab| + {1:++}ababab | + {24:-- VISUAL --} | + ]]} + + feed('<Esc>$') + command('set number showbreak=') + screen:expect{grid=[[ + {2: 1 }ababababababababababababababababababababab{4:0123}| + {2: }{4:456789}abababababababababababababababababababa{4:0}| + {2: }{4:123456789}babababababababababababababababababab| + {2: }ababababababababababababababababababababababab| + {2: }abababababababa^b | + | + ]]} + + command('set cpoptions+=n') + screen:expect{grid=[[ + {2: 1 }ababababababababababababababababababababab{4:0123}| + {4:456789}abababababababababababababababababababa{4:01234}| + {4:56789}babababababababababababababababababababababab| + ababababababababababababababababababababababababab| + aba^b | + | + ]]} + + feed('0g$hi<Tab>') + screen:expect{grid=[[ + {2: 1 }ababababababababababababababababababababab{4:01} | + {4:^23456789}abababababababababababababababababababa{4:0}| + {4:123456789}babababababababababababababababababababab| + ababababababababababababababababababababababababab| + abababab | + {24:-- INSERT --} | + ]]} + end) + it('virt_text_hide hides overlay virtual text when extmark is off-screen', function() screen:try_resize(50, 3) command('set nowrap') @@ -1027,12 +1131,12 @@ describe('extmark decorations', function() it('can have virtual text of right_align and 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, 1, 0, { virt_text={{'Very', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) meths.buf_set_extmark(0, ns, 1, 0, { virt_text={{'VERY', 'ErrorMsg'}}, virt_text_pos='right_align', 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, 2, 10, { virt_text={{'Much', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) meths.buf_set_extmark(0, ns, 2, 10, { virt_text={{'MUCH', 'ErrorMsg'}}, virt_text_pos='right_align', 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, 3, 15, { virt_text={{'ERROR', 'ErrorMsg'}}, virt_text_pos='right_align', hl_mode='blend'}) + meths.buf_set_extmark(0, ns, 3, 14, { virt_text={{'Error', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) + meths.buf_set_extmark(0, ns, 3, 14, { virt_text={{'ERROR', 'ErrorMsg'}}, virt_text_pos='right_align', hl_mode='blend'}) meths.buf_set_extmark(0, ns, 7, 21, { virt_text={{'-', 'NonText'}}, virt_text_win_col=4, hl_mode='blend'}) meths.buf_set_extmark(0, ns, 7, 21, { virt_text={{'-', 'NonText'}}, virt_text_pos='right_align', hl_mode='blend'}) -- empty virt_text should not change anything @@ -1190,40 +1294,78 @@ describe('extmark decorations', function() | ]]} - command 'set cpoptions-=n nonumber nowrap' + command 'set cpoptions-=n nowrap' screen:expect{grid=[[ - for _,item in ipairs(items) do | - local text, hl_id_cell, cou{4:Very} unpack(ite{4:VERY}| - if | - hl_id_cell ~= nil then {4:Much} {4:MUCH}| - --^ -- -- -- -- -- -- --{4:Error}- -- -- h{4:ERROR}| - end | - for _ = 1, (count or 1) do | - local cell = line[colpos] | - {1:-} cell.text = text {1:-}| - cell.hl_id = hl_id | - colpos = colpos+1 | - end | - end | + {2: 1 }for _,item in ipairs(items) do | + {2: 2 } local text, hl_id_cell, cou{4:Very} unpack{4:VERY}| + {2: 3 } if | + {2: 4 }hl_id_cell ~= nil then {4:Much} {4:MUCH}| + {2: 5 } --^ -- -- -- -- -- -- --{4:Error}- -- {4:ERROR}| + {2: 6 } end | + {2: 7 } for _ = 1, (count or 1) do | + {2: 8 } local cell = line[colpos] | + {2: 9 } {1:-} cell.text = text {1:-}| + {2: 10 } cell.hl_id = hl_id | + {2: 11 } colpos = colpos+1 | + {2: 12 } end | + {2: 13 }end | {1:~ }| | ]]} - feed '8zl' + feed '12zl' screen:expect{grid=[[ - em in ipairs(items) do | - l text, hl_id_cell, count = unp{4:Very}item) {4:VERY}| - | - ll ~= nil then {4:Much} {4:MUCH}| - --^ -- -- -- -- -- -- -- -- -- -{4:Error}hl_id = h{4:ERROR}| - | - _ = 1, (count or 1) do | - local cell = line[colpos] | - cell{1:-}text = text {1:-}| - cell.hl_id = hl_id | - colpos = colpos+1 | - | + {2: 1 }n ipairs(items) do | + {2: 2 }xt, hl_id_cell, count = unpack({4:Very}) {4:VERY}| + {2: 3 } | + {2: 4 }= nil then {4:Much} {4:MUCH}| + {2: 5 }^- -- -- -- -- -- -- -- -- -- --{4:Error}d = h{4:ERROR}| + {2: 6 } | + {2: 7 }1, (count or 1) do | + {2: 8 }l cell = line[colpos] | + {2: 9 }.tex{1:-} = text {1:-}| + {2: 10 }.hl_id = hl_id | + {2: 11 }os = colpos+1 | + {2: 12 } | + {2: 13 } | + {1:~ }| | + ]]} + + feed('fhi<Tab>') + screen:expect{grid=[[ + {2: 1 }n ipairs(items) do | + {2: 2 }xt, hl_id_cell, count = unpack({4:Very}) {4:VERY}| + {2: 3 } | + {2: 4 }= nil then {4:Much} {4:MUCH}| + {2: 5 }- -- -- -- -- -- -- -- -- -- --{4:Error}^hl_id{4:ERROR}| + {2: 6 } | + {2: 7 }1, (count or 1) do | + {2: 8 }l cell = line[colpos] | + {2: 9 }.tex{1:-} = text {1:-}| + {2: 10 }.hl_id = hl_id | + {2: 11 }os = colpos+1 | + {2: 12 } | + {2: 13 } | + {1:~ }| + {24:-- INSERT --} | + ]]} + + feed('<Esc>0') + screen:expect{grid=[[ + {2: 1 }for _,item in ipairs(items) do | + {2: 2 } local text, hl_id_cell, cou{4:Very} unpack{4:VERY}| + {2: 3 } if | + {2: 4 }hl_id_cell ~= nil then {4:Much} {4:MUCH}| + {2: 5 }^ -- -- -- -- -- -- -- --{4:Error}- -- {4:ERROR}| + {2: 6 } end | + {2: 7 } for _ = 1, (count or 1) do | + {2: 8 } local cell = line[colpos] | + {2: 9 } {1:-} cell.text = text {1:-}| + {2: 10 } cell.hl_id = hl_id | + {2: 11 } colpos = colpos+1 | + {2: 12 } end | + {2: 13 }end | {1:~ }| | ]]} @@ -1235,20 +1377,21 @@ describe('extmark decorations', function() 22222 33333]]) command('1,2fold') - command('set nowrap') screen:try_resize(50, 3) feed('zb') -- XXX: the behavior of overlay virtual text at non-zero column is strange: -- 1. With 'wrap' it is never shown. -- 2. With 'nowrap' it is shown only if the extmark is hidden before leftcol. meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'AA', 'Underlined'}}, hl_mode = 'combine', virt_text_pos = 'overlay' }) - meths.buf_set_extmark(0, ns, 0, 1, { virt_text = {{'BB', 'Underlined'}}, hl_mode = 'combine', virt_text_win_col = 10 }) + meths.buf_set_extmark(0, ns, 0, 5, { virt_text = {{'BB', 'Underlined'}}, hl_mode = 'combine', virt_text_win_col = 10 }) meths.buf_set_extmark(0, ns, 0, 2, { virt_text = {{'CC', 'Underlined'}}, hl_mode = 'combine', virt_text_pos = 'right_align' }) screen:expect{grid=[[ {29:AA}{33:- 2 lin}{29:BB}{33:: 11111·····························}{29:CC}| 3333^3 | | ]]} + command('set nowrap') + screen:expect_unchanged() feed('zl') screen:expect{grid=[[ {29:AA}{33:- 2 lin}{29:BB}{33:: 11111·····························}{29:CC}| @@ -1269,6 +1412,38 @@ describe('extmark decorations', function() ]]} end) + it('virtual text works below diff filler lines', function() + screen:try_resize(53, 8) + insert([[ + aaaaa + bbbbb + ccccc + ddddd + eeeee]]) + command('rightbelow vnew') + insert([[ + bbbbb + ccccc + ddddd + eeeee]]) + command('windo diffthis') + meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'AA', 'Underlined'}}, virt_text_pos = 'overlay' }) + meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'BB', 'Underlined'}}, virt_text_win_col = 10 }) + meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'CC', 'Underlined'}}, virt_text_pos = 'right_align' }) + screen:expect{grid=[[ + {37: }{38:aaaaa }│{37: }{39:------------------------}| + {37: }bbbbb │{37: }{28:AA}bbb {28:BB} {28:CC}| + {37: }ccccc │{37: }ccccc | + {37: }ddddd │{37: }ddddd | + {37: }eeeee │{37: }eeee^e | + {1:~ }│{1:~ }| + {40:[No Name] [+] }{41:[No Name] [+] }| + | + ]]} + command('windo set wrap') + screen:expect_unchanged() + end) + it('can have virtual text which combines foreground and background groups', function() screen:set_default_attr_ids { [1] = {bold=true, foreground=Screen.colors.Blue}; @@ -1652,6 +1827,70 @@ describe('extmark decorations', function() {24:-- VISUAL BLOCK --} | ]]) end) + + it('supports multiline highlights', function() + insert(example_text) + feed 'gg' + for _,i in ipairs {1,2,3,5,6,7} do + for _,j in ipairs {2,5,10,15} do + meths.buf_set_extmark(0, ns, i, j, { end_col=j+2, hl_group = 'NonText'}) + end + end + screen:expect{grid=[[ + ^for _,item in ipairs(items) do | + {1: }l{1:oc}al {1:te}xt,{1: h}l_id_cell, count = unpack(item) | + {1: }i{1:f }hl_{1:id}_ce{1:ll} ~= nil then | + {1: } {1: } hl{1:_i}d ={1: h}l_id_cell | + end | + {1: }f{1:or} _ {1:= }1, {1:(c}ount or 1) do | + {1: } {1: } lo{1:ca}l c{1:el}l = line[colpos] | + {1: } {1: } ce{1:ll}.te{1:xt} = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + {1:~ }| + | + ]]} + feed'5<c-e>' + screen:expect{grid=[[ + ^ {1: }f{1:or} _ {1:= }1, {1:(c}ount or 1) do | + {1: } {1: } lo{1:ca}l c{1:el}l = line[colpos] | + {1: } {1: } ce{1:ll}.te{1:xt} = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + meths.buf_set_extmark(0, ns, 1, 0, { end_line=8, end_col=10, hl_group = 'ErrorMsg'}) + screen:expect{grid=[[ + {4:^ }{36: }{4:f}{36:or}{4: _ }{36:= }{4:1, }{36:(c}{4:ount or 1) do} | + {4: }{36: }{4: }{36: }{4: lo}{36:ca}{4:l c}{36:el}{4:l = line[colpos]} | + {4: }{36: }{4: }{36: }{4: ce}{36:ll}{4:.te}{36:xt}{4: = text} | + {4: ce}ll.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: inline virtual text', function() @@ -4136,7 +4375,6 @@ l5 end) it('can add multiple signs (single extmark)', function() - pending('TODO(lewis6991): Support ranged signs') insert(example_test3) feed 'gg' @@ -4158,7 +4396,6 @@ l5 end) it('can add multiple signs (multiple extmarks)', function() - pending('TODO(lewis6991): Support ranged signs') insert(example_test3) feed'gg' @@ -4219,7 +4456,6 @@ l5 end) it('can add multiple signs (multiple extmarks) 3', function() - pending('TODO(lewis6991): Support ranged signs') insert(example_test3) feed 'gg' @@ -4289,7 +4525,6 @@ l5 end) it('works with old signs (with range)', function() - pending('TODO(lewis6991): Support ranged signs') insert(example_test3) feed 'gg' @@ -4304,7 +4539,7 @@ l5 screen:expect{grid=[[ S3S4S1^l1 | - S2S3x l2 | + x S2S3l2 | S5S3{1: }l3 | S3{1: }l4 | S3{1: }l5 | @@ -4317,8 +4552,6 @@ l5 end) it('can add a ranged sign (with start out of view)', function() - pending('TODO(lewis6991): Support ranged signs') - insert(example_test3) command 'set signcolumn=yes:2' feed 'gg' diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 0cf8a124ff..e37b3ccb5f 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -3319,7 +3319,6 @@ describe('float window', function() ]]) end - meths.win_set_config(win, {relative='win', win=oldwin, row=1, col=10, anchor='NW'}) if multigrid then screen:expect{grid=[[ @@ -3765,6 +3764,198 @@ describe('float window', function() end end) + it('anchored to another floating window updated in the same call #14735', function() + feed('i<CR><CR><CR><Esc>') + + exec([[ + let b1 = nvim_create_buf(v:true, v:false) + let b2 = nvim_create_buf(v:true, v:false) + let b3 = nvim_create_buf(v:true, v:false) + let b4 = nvim_create_buf(v:true, v:false) + let b5 = nvim_create_buf(v:true, v:false) + let b6 = nvim_create_buf(v:true, v:false) + let b7 = nvim_create_buf(v:true, v:false) + let b8 = nvim_create_buf(v:true, v:false) + call setbufline(b1, 1, '1') + call setbufline(b2, 1, '2') + call setbufline(b3, 1, '3') + call setbufline(b4, 1, '4') + call setbufline(b5, 1, '5') + call setbufline(b6, 1, '6') + call setbufline(b7, 1, '7') + call setbufline(b8, 1, '8') + let o1 = #{relative: 'editor', row: 1, col: 10, width: 5, height: 1} + let w1 = nvim_open_win(b1, v:false, o1) + let o2 = extendnew(o1, #{col: 30}) + let w2 = nvim_open_win(b2, v:false, o2) + let o3 = extendnew(o1, #{relative: 'win', win: w1, anchor: 'NE', col: 0}) + let w3 = nvim_open_win(b3, v:false, o3) + let o4 = extendnew(o3, #{win: w2}) + let w4 = nvim_open_win(b4, v:false, o4) + let o5 = extendnew(o3, #{win: w3, anchor: 'SE', row: 0}) + let w5 = nvim_open_win(b5, v:false, o5) + let o6 = extendnew(o5, #{win: w4}) + let w6 = nvim_open_win(b6, v:false, o6) + let o7 = extendnew(o5, #{win: w5, anchor: 'SW', col: 5}) + let w7 = nvim_open_win(b7, v:false, o7) + let o8 = extendnew(o7, #{win: w6}) + let w8 = nvim_open_win(b8, v:false, o8) + ]]) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + | + | + | + ^ | + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 5 + {1:1 }| + ## grid 6 + {1:2 }| + ## grid 7 + {1:3 }| + ## grid 8 + {1:4 }| + ## grid 9 + {1:5 }| + ## grid 10 + {1:6 }| + ## grid 11 + {1:7 }| + ## grid 12 + {1:8 }| + ]], float_pos={ + [5] = {{id = 1002}, "NW", 1, 1, 10, true, 50}; + [6] = {{id = 1003}, "NW", 1, 1, 30, true, 50}; + [7] = {{id = 1004}, "NE", 5, 1, 0, true, 50}; + [8] = {{id = 1005}, "NE", 6, 1, 0, true, 50}; + [9] = {{id = 1006}, "SE", 7, 0, 0, true, 50}; + [10] = {{id = 1007}, "SE", 8, 0, 0, true, 50}; + [11] = {{id = 1008}, "SW", 9, 0, 5, true, 50}; + [12] = {{id = 1009}, "SW", 10, 0, 5, true, 50}; + }} + else + screen:expect([[ + {1:7 } {1:8 } | + {1:5 } {1:1 } {1:6 } {1:2 } | + {1:3 } {1:4 } | + ^ | + {0:~ }| + {0:~ }| + | + ]]) + end + + -- Reconfigure in different directions + exec([[ + let o1 = extendnew(o1, #{anchor: 'NW'}) + call nvim_win_set_config(w8, o1) + let o2 = extendnew(o2, #{anchor: 'NW'}) + call nvim_win_set_config(w4, o2) + let o3 = extendnew(o3, #{win: w8}) + call nvim_win_set_config(w2, o3) + let o4 = extendnew(o4, #{win: w4}) + call nvim_win_set_config(w1, o4) + let o5 = extendnew(o5, #{win: w2}) + call nvim_win_set_config(w6, o5) + let o6 = extendnew(o6, #{win: w1}) + call nvim_win_set_config(w3, o6) + let o7 = extendnew(o7, #{win: w6}) + call nvim_win_set_config(w5, o7) + let o8 = extendnew(o8, #{win: w3}) + call nvim_win_set_config(w7, o8) + ]]) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + | + | + | + ^ | + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 5 + {1:1 }| + ## grid 6 + {1:2 }| + ## grid 7 + {1:3 }| + ## grid 8 + {1:4 }| + ## grid 9 + {1:5 }| + ## grid 10 + {1:6 }| + ## grid 11 + {1:7 }| + ## grid 12 + {1:8 }| + ]], float_pos={ + [5] = {{id = 1002}, "NE", 8, 1, 0, true, 50}; + [6] = {{id = 1003}, "NE", 12, 1, 0, true, 50}; + [7] = {{id = 1004}, "SE", 5, 0, 0, true, 50}; + [8] = {{id = 1005}, "NW", 1, 1, 30, true, 50}; + [9] = {{id = 1006}, "SW", 10, 0, 5, true, 50}; + [10] = {{id = 1007}, "SE", 6, 0, 0, true, 50}; + [11] = {{id = 1008}, "SW", 7, 0, 5, true, 50}; + [12] = {{id = 1009}, "NW", 1, 1, 10, true, 50}; + }} + else + screen:expect([[ + {1:5 } {1:7 } | + {1:6 } {1:8 } {1:3 } {1:4 } | + {1:2 } {1:1 } | + ^ | + {0:~ }| + {0:~ }| + | + ]]) + end + + -- Not clear how cycles should behave, but they should not hang or crash + exec([[ + let o1 = extendnew(o1, #{relative: 'win', win: w7}) + call nvim_win_set_config(w1, o1) + let o2 = extendnew(o2, #{relative: 'win', win: w8}) + call nvim_win_set_config(w2, o2) + let o3 = extendnew(o3, #{win: w1}) + call nvim_win_set_config(w3, o3) + let o4 = extendnew(o4, #{win: w2}) + call nvim_win_set_config(w4, o4) + let o5 = extendnew(o5, #{win: w3}) + call nvim_win_set_config(w5, o5) + let o6 = extendnew(o6, #{win: w4}) + call nvim_win_set_config(w6, o6) + let o7 = extendnew(o7, #{win: w5}) + call nvim_win_set_config(w7, o7) + let o8 = extendnew(o8, #{win: w6}) + call nvim_win_set_config(w8, o8) + redraw + ]]) + end) + it('can be placed relative text in a window', function() screen:try_resize(30,5) local firstwin = meths.get_current_win().id diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 2307fd106b..be7c2f291c 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -1270,7 +1270,7 @@ end function Screen:render(headers, attr_state, preview) headers = headers and (self._options.ext_multigrid or self._options._debug_float) local rv = {} - for igrid,grid in pairs(self._grids) do + for igrid,grid in vim.spairs(self._grids) do if headers then local suffix = "" if igrid > 1 and self.win_position[igrid] == nil diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua index ee235cd6b5..742976cbe2 100644 --- a/test/functional/ui/statuscolumn_spec.lua +++ b/test/functional/ui/statuscolumn_spec.lua @@ -532,6 +532,24 @@ describe('statuscolumn', function() eq('0 3 r 7', eval("g:testvar")) meths.input_mouse('right', 'press', '', 0, 3, 0) eq('0 4 r 7', eval("g:testvar")) + + command('rightbelow vsplit') + meths.input_mouse('left', 'press', '', 0, 0, 27) + eq('0 1 l 4', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 3, 27) + eq('0 1 r 7', eval("g:testvar")) + command('setlocal rightleft') + meths.input_mouse('left', 'press', '', 0, 0, 52) + eq('0 1 l 4', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 3, 52) + eq('0 1 r 7', eval("g:testvar")) + command('wincmd H') + meths.input_mouse('left', 'press', '', 0, 0, 25) + eq('0 1 l 4', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 3, 25) + eq('0 1 r 7', eval("g:testvar")) + command('close') + command('set laststatus=2 winbar=%f') command('let g:testvar = ""') -- Check that winbar click doesn't register as statuscolumn click diff --git a/test/functional/vimscript/system_spec.lua b/test/functional/vimscript/system_spec.lua index 762e8877ce..90aab48d61 100644 --- a/test/functional/vimscript/system_spec.lua +++ b/test/functional/vimscript/system_spec.lua @@ -335,12 +335,12 @@ describe('system()', function() if is_os('win') then eq("echoed\n", eval('system("echo echoed")')) else - eq("echoed", eval('system("echo -n echoed")')) + eq("echoed", eval('system("printf echoed")')) end end) it('to backgrounded command does not crash', function() -- This is indeterminate, just exercise the codepath. May get E5677. - feed_command('call system(has("win32") ? "start /b /wait cmd /c echo echoed" : "echo -n echoed &")') + feed_command('call system(has("win32") ? "start /b /wait cmd /c echo echoed" : "printf echoed &")') local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") if v_errnum then eq("E5677:", v_errnum) diff --git a/test/old/testdir/check.vim b/test/old/testdir/check.vim index 281514db17..af1a80250c 100644 --- a/test/old/testdir/check.vim +++ b/test/old/testdir/check.vim @@ -100,6 +100,14 @@ func CheckLinux() endif endfunc +" Command to check for not running on a BSD system. +command CheckNotBSD call CheckNotBSD() +func CheckNotBSD() + if has('bsd') + throw 'Skipped: does not work on BSD' + endif +endfunc + " Command to check that making screendumps is supported. " Caller must source screendump.vim command CheckScreendump call CheckScreendump() diff --git a/test/old/testdir/test_autocmd.vim b/test/old/testdir/test_autocmd.vim index 79f8ee43c1..0afa3417ec 100644 --- a/test/old/testdir/test_autocmd.vim +++ b/test/old/testdir/test_autocmd.vim @@ -2002,8 +2002,8 @@ endfunc " Test for BufUnload autocommand that unloads all the other buffers func Test_bufunload_all() let g:test_is_flaky = 1 - call writefile(['Test file Xxx1'], 'Xxx1', 'D')" - call writefile(['Test file Xxx2'], 'Xxx2', 'D')" + call writefile(['Test file Xxx1'], 'Xxx1', 'D') + call writefile(['Test file Xxx2'], 'Xxx2', 'D') let content =<< trim [CODE] func UnloadAllBufs() diff --git a/test/old/testdir/test_edit.vim b/test/old/testdir/test_edit.vim index 0f3c62a092..03796ad816 100644 --- a/test/old/testdir/test_edit.vim +++ b/test/old/testdir/test_edit.vim @@ -1235,7 +1235,7 @@ func Test_edit_LEFT_RIGHT() endfunc func Test_edit_MOUSE() - " This is a simple test, since we not really using the mouse here + " This is a simple test, since we're not really using the mouse here CheckFeature mouse 10new call setline(1, range(1, 100)) @@ -1817,7 +1817,7 @@ func Test_edit_charconvert() close! set charconvert& - " 'charconvert' function doesn't create a output file + " 'charconvert' function doesn't create an output file func Cconv1() endfunc set charconvert=Cconv1() diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim index 1dd255ccf9..b2b6ad80bb 100644 --- a/test/old/testdir/test_filetype.vim +++ b/test/old/testdir/test_filetype.vim @@ -427,6 +427,7 @@ func s:GetFilenameChecks() abort \ 'mrxvtrc': ['mrxvtrc', '.mrxvtrc'], \ 'msidl': ['file.odl', 'file.mof'], \ 'msql': ['file.msql'], + \ 'mojo': ['file.mojo', 'file.🔥'], \ 'mupad': ['file.mu'], \ 'mush': ['file.mush'], \ 'muttrc': ['Muttngrc', 'Muttrc', '.muttngrc', '.muttngrc-file', '.muttrc', '.muttrc-file', '/.mutt/muttngrc', '/.mutt/muttngrc-file', '/.mutt/muttrc', '/.mutt/muttrc-file', '/.muttng/muttngrc', '/.muttng/muttngrc-file', '/.muttng/muttrc', '/.muttng/muttrc-file', '/etc/Muttrc.d/file', '/etc/Muttrc.d/file.rc', 'Muttngrc-file', 'Muttrc-file', 'any/.mutt/muttngrc', 'any/.mutt/muttngrc-file', 'any/.mutt/muttrc', 'any/.mutt/muttrc-file', 'any/.muttng/muttngrc', 'any/.muttng/muttngrc-file', 'any/.muttng/muttrc', 'any/.muttng/muttrc-file', 'any/etc/Muttrc.d/file', 'muttngrc', 'muttngrc-file', 'muttrc', 'muttrc-file'], diff --git a/test/old/testdir/test_functions.vim b/test/old/testdir/test_functions.vim index eff4e36f34..3e1e5a4816 100644 --- a/test/old/testdir/test_functions.vim +++ b/test/old/testdir/test_functions.vim @@ -290,6 +290,7 @@ endfunc func Test_strptime() CheckFunction strptime + CheckNotBSD CheckNotMSWindows if exists('$TZ') @@ -305,6 +306,8 @@ func Test_strptime() call assert_fails('call strptime()', 'E119:') call assert_fails('call strptime("xxx")', 'E119:') + " This fails on BSD 14 and returns + " -2209078800 instead of 0 call assert_equal(0, strptime("%Y", '')) call assert_equal(0, strptime("%Y", "xxx")) @@ -2630,7 +2633,7 @@ func Test_state() call term_sendkeys(buf, getstate) call WaitForAssert({-> assert_match('state: mSc; mode: n', term_getline(buf, 6))}, 1000) - " A operator is pending + " An operator is pending call term_sendkeys(buf, ":call RunTimer()\<CR>y") call TermWait(buf, 25) call term_sendkeys(buf, "y") diff --git a/test/old/testdir/test_listdict.vim b/test/old/testdir/test_listdict.vim index 0918e2cd98..fef7c6a9bc 100644 --- a/test/old/testdir/test_listdict.vim +++ b/test/old/testdir/test_listdict.vim @@ -346,7 +346,7 @@ func Test_dict_big() endtry call assert_equal('Vim(let):E716: "1500"', str) - " lookup each items + " lookup each item for i in range(1500) call assert_equal(3000 - i, d[i]) endfor diff --git a/test/old/testdir/test_normal.vim b/test/old/testdir/test_normal.vim index 08a3c57a94..3c21041899 100644 --- a/test/old/testdir/test_normal.vim +++ b/test/old/testdir/test_normal.vim @@ -347,7 +347,7 @@ func Test_normal06_formatprg() CheckNotMSWindows " uses sed to number non-empty lines - call writefile(['#!/bin/sh', 'sed ''/./=''|sed ''/./{', 'N', 's/\n/ /', '}'''], 'Xsed_format.sh') + call writefile(['#!/bin/sh', 'sed ''/./=''|sed ''/./{', 'N', 's/\n/ /', '}'''], 'Xsed_format.sh', 'D') call system('chmod +x ./Xsed_format.sh') let text = ['a', '', 'c', '', ' ', 'd', 'e'] let expected = ['1 a', '', '3 c', '', '5 ', '6 d', '7 e'] @@ -378,11 +378,10 @@ func Test_normal06_formatprg() " clean up set formatprg= setlocal formatprg= - call delete('Xsed_format.sh') endfunc func Test_normal07_internalfmt() - " basic test for internal formmatter to textwidth of 12 + " basic test for internal formatter to textwidth of 12 let list=range(1,11) call map(list, 'v:val." "') 10new @@ -2588,7 +2587,7 @@ func Test_normal33_g_cmd2() exe "norm! G0\<c-v>4k4ly" exe "norm! gvood" call assert_equal(['', 'abfgh', 'abfgh', 'abfgh', 'fgh', 'fgh', 'fgh', 'fgh', 'fgh'], getline(1,'$')) - " gv cannot be used in operator pending mode + " gv cannot be used in operator pending mode call assert_beeps('normal! cgv') " gv should beep without a previously selected visual area new diff --git a/test/old/testdir/test_substitute.vim b/test/old/testdir/test_substitute.vim index a6640aac30..8dff0cda52 100644 --- a/test/old/testdir/test_substitute.vim +++ b/test/old/testdir/test_substitute.vim @@ -1425,4 +1425,18 @@ func Test_z_substitute_expr_leak() delfunc SubExpr endfunc +func Test_substitute_expr_switch_win() + func R() + wincmd x + return 'XXXX' + endfunc + new Xfoobar + let bufnr = bufnr('%') + put ="abcdef" + silent! s/\%')/\=R() + call assert_fails(':%s/./\=R()/g', 'E565:') + delfunc R + exe bufnr .. "bw!" +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_vimscript.vim b/test/old/testdir/test_vimscript.vim index 9b1b7f7b23..6ce59e1a2e 100644 --- a/test/old/testdir/test_vimscript.vim +++ b/test/old/testdir/test_vimscript.vim @@ -3109,7 +3109,7 @@ endfunc " should be given. " " This test reuses the function MESSAGES() from the previous test. -" This functions checks the messages in g:msgfile. +" This function checks the messages in g:msgfile. "------------------------------------------------------------------------------- func Test_nested_while_error() @@ -3236,7 +3236,7 @@ endfunc " error messages should be given. " " This test reuses the function MESSAGES() from the previous test. -" This functions checks the messages in g:msgfile. +" This function checks the messages in g:msgfile. "------------------------------------------------------------------------------- func Test_nested_cont_break_error() @@ -3344,7 +3344,7 @@ endfunc " should be given. " " This test reuses the function MESSAGES() from the previous test. -" This functions checks the messages in g:msgfile. +" This function check the messages in g:msgfile. "------------------------------------------------------------------------------- func Test_nested_endtry_error() diff --git a/test/old/testdir/test_virtualedit.vim b/test/old/testdir/test_virtualedit.vim index f97b3f987d..6ff51e36fb 100644 --- a/test/old/testdir/test_virtualedit.vim +++ b/test/old/testdir/test_virtualedit.vim @@ -236,7 +236,7 @@ func Test_ve_completion() set virtualedit= endfunc -" Using "C" then then <CR> moves the last remaining character to the next +" Using "C" then <CR> moves the last remaining character to the next " line. (Mary Ellen Foster) func Test_ve_del_to_eol() new diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index e9b97266d0..43b6980702 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -849,6 +849,16 @@ local function ptr2key(ptr) return ffi.string(s) end +local function is_asan() + cimport('./src/nvim/version.h') + local status, res = pcall(function() return lib.version_cflags end) + if status then + return ffi.string(res):match('-fsanitize=[a-z,]*address') + else + return false + end +end + local module = { cimport = cimport, cppimport = cppimport, @@ -876,6 +886,7 @@ local module = { ptr2addr = ptr2addr, ptr2key = ptr2key, debug_log = debug_log, + is_asan = is_asan, } module = global_helpers.tbl_extend('error', module, global_helpers) return function() diff --git a/test/unit/marktree_spec.lua b/test/unit/marktree_spec.lua index 3c96bc5f58..32300c167c 100644 --- a/test/unit/marktree_spec.lua +++ b/test/unit/marktree_spec.lua @@ -87,13 +87,18 @@ local function dosplice(tree, shadow, start, old_extent, new_extent) shadowsplice(shadow, start, old_extent, new_extent) end +local ns = 10 local last_id = nil -local function put(tree, row, col, gravitate) +local function put(tree, row, col, gravitate, end_row, end_col, end_gravitate) last_id = last_id + 1 local my_id = last_id - lib.marktree_put_test(tree, my_id, row, col, gravitate); + end_row = end_row or -1 + end_col = end_col or -1 + end_gravitate = end_gravitate or false + + lib.marktree_put_test(tree, ns, my_id, row, col, gravitate, end_row, end_col, end_gravitate); return my_id end @@ -102,7 +107,7 @@ describe('marktree', function() last_id = 0 end) - itp('works', function() + itp('works', function() local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit local shadow = {} local iter = ffi.new("MarkTreeIter[1]") @@ -129,7 +134,7 @@ describe('marktree', function() eq({}, id2pos) for i,ipos in pairs(shadow) do - local p = lib.marktree_lookup_ns(tree, -1, i, false, iter) + local p = lib.marktree_lookup_ns(tree, ns, i, false, iter) eq(ipos[1], p.pos.row) eq(ipos[2], p.pos.col) local k = lib.marktree_itr_current(iter) @@ -210,10 +215,224 @@ describe('marktree', function() lib.marktree_itr_get(tree, 10, 10, iter) lib.marktree_del_itr(tree, iter, false) - eq(11, iter[0].node.key[iter[0].i].pos.col) + eq(11, iter[0].x.key[iter[0].i].pos.col) lib.marktree_itr_get(tree, 11, 11, iter) lib.marktree_del_itr(tree, iter, false) - eq(12, iter[0].node.key[iter[0].i].pos.col) - end) + eq(12, iter[0].x.key[iter[0].i].pos.col) + end) + + itp("'intersect_mov' function works correctly", function() + local function mov(x, y, w) + local xa = ffi.new("uint64_t[?]", #x) + for i, xi in ipairs(x) do xa[i-1] = xi end + local ya = ffi.new("uint64_t[?]", #y) + for i, yi in ipairs(y) do ya[i-1] = yi end + local wa = ffi.new("uint64_t[?]", #w) + for i, wi in ipairs(w) do wa[i-1] = wi end + + local dummy_size = #x + #y + #w + local wouta = ffi.new("uint64_t[?]", dummy_size) + local douta = ffi.new("uint64_t[?]", dummy_size) + local wsize = ffi.new("size_t[1]") + wsize[0] = dummy_size + local dsize = ffi.new("size_t[1]") + dsize[0] = dummy_size + + local status = lib.intersect_mov_test(xa, #x, ya, #y, wa, #w, wouta, wsize, douta, dsize) + if status == 0 then error'wowza' end + + local wout, dout = {}, {} + for i = 0,tonumber(wsize[0])-1 do table.insert(wout, tonumber(wouta[i])) end + for i = 0,tonumber(dsize[0])-1 do table.insert(dout, tonumber(douta[i])) end + return {wout, dout} + end + + eq({{}, {}}, mov({}, {2, 3}, {2, 3})) + eq({{2, 3}, {}}, mov({}, {}, {2, 3})) + eq({{2, 3}, {}}, mov({2, 3}, {}, {})) + eq({{}, {2,3}}, mov({}, {2,3}, {})) + + eq({{1, 5}, {}}, mov({1,2,5}, {2, 3}, {3})) + eq({{1, 2}, {}}, mov({1,2,5}, {5, 10}, {10})) + eq({{1, 2}, {5}}, mov({1,2}, {5, 10}, {10})) + eq({{1,3,5,7,9}, {2,4,6,8,10}}, mov({1,3,5,7,9}, {2,4,6,8,10}, {})) + eq({{1,3,5,7,9}, {2,6,10}}, mov({1,3,5,7,9}, {2,4,6,8,10}, {4, 8})) + eq({{1,4,7}, {2,5,8}}, mov({1,3,4,6,7,9}, {2,3,5,6,8,9}, {})) + eq({{1,4,7}, {}}, mov({1,3,4,6,7,9}, {2,3,5,6,8,9}, {2,5,8})) + eq({{0,1,4,7,10}, {}}, mov({1,3,4,6,7,9}, {2,3,5,6,8,9}, {0,2,5,8,10})) + end) + + + local function check_intersections(tree) + lib.marktree_check(tree) + -- to debug stuff disable this branch + if true == true then + ok(lib.marktree_check_intersections(tree)) + return + end + + local str1 = lib.mt_inspect(tree, true, true) + local dot1 = ffi.string(str1.data, str1.size) + + local val = lib.marktree_check_intersections(tree) + if not val then + local str2 = lib.mt_inspect(tree, true, true) + local dot2 = ffi.string(str2.data, str2.size) + print("actual:\n\n".."Xafile.dot".."\n\nexpected:\n\n".."Xefile.dot".."\n") + print("nivå", tree[0].root.level); + io.stdout:flush() + local afil = io.open("Xafile.dot", "wb") + afil:write(dot1) + afil:close() + local efil = io.open("Xefile.dot", "wb") + efil:write(dot2) + efil:close() + ok(false) + else + ffi.C.xfree(str1.data) + end + end + + itp('works with intersections', function() + local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit + + local ids = {} + + for i = 1,80 do + table.insert(ids, put(tree, 1, i, false, 2, 100-i, false)) + check_intersections(tree) + end + for i = 1,80 do + lib.marktree_del_pair_test(tree, ns, ids[i]) + check_intersections(tree) + end + ids = {} + + for i = 1,80 do + table.insert(ids, put(tree, 1, i, false, 2, 100-i, false)) + check_intersections(tree) + end + + for i = 1,10 do + for j = 1,8 do + local ival = (j-1)*10+i + lib.marktree_del_pair_test(tree, ns, ids[ival]) + check_intersections(tree) + end + end + end) + + itp('works with intersections with a big tree', function() + local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit + + local ids = {} + + for i = 1,1000 do + table.insert(ids, put(tree, 1, i, false, 2, 1000-i, false)) + if i % 10 == 1 then + check_intersections(tree) + end + end + + check_intersections(tree) + eq(2000, tree[0].n_keys) + ok(tree[0].root.level >= 2) + + local iter = ffi.new("MarkTreeIter[1]") + + local k = 0 + for i = 1,20 do + for j = 1,50 do + k = k + 1 + local ival = (j-1)*20+i + if false == true then -- if there actually is a failure, this branch will fail out at the actual spot of the error + lib.marktree_lookup_ns(tree, ns, ids[ival], false, iter) + lib.marktree_del_itr(tree, iter, false) + check_intersections(tree) + + lib.marktree_lookup_ns(tree, ns, ids[ival], true, iter) + lib.marktree_del_itr(tree, iter, false) + check_intersections(tree) + else + lib.marktree_del_pair_test(tree, ns, ids[ival]) + if k % 5 == 1 then + check_intersections(tree) + end + end + end + end + + eq(0, tree[0].n_keys) + end) + + itp('works with intersections with a even bigger tree', function() + local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit + + local ids = {} + + -- too much overhead on ASAN + local size_factor = helpers.is_asan() and 3 or 10 + + local at_row = {} + for i = 1, 10 do + at_row[i] = {} + end + + local size = 1000*size_factor + local k = 1 + while k <= size do + for row1 = 1,9 do + for row2 = row1,10 do -- note row2 can be == row1, leads to empty ranges being tested when k > size/2 + if k > size then + break + end + local id = put(tree, row1, k, false, row2, size-k, false) + table.insert(ids, id) + for i = row1+1, row2 do + table.insert(at_row[i], id) + end + --if tree[0].root.level == 4 then error("kk"..k) end + if k % 100*size_factor == 1 or (k < 2000 and k%100 == 1) then + check_intersections(tree) + end + k = k + 1 + end + end + end + + eq(2*size, tree[0].n_keys) + ok(tree[0].root.level >= 3) + check_intersections(tree) + + local iter = ffi.new("MarkTreeIter[1]") + local pair = ffi.new("MTPair[1]") + for i = 1,10 do + -- use array as set and not {[id]=true} map, to detect duplicates + local set = {} + eq(true, ffi.C.marktree_itr_get_overlap(tree, i, 0, iter)) + while ffi.C.marktree_itr_step_overlap(tree, iter, pair) do + local id = tonumber(pair[0].start.id) + table.insert(set, id) + end + table.sort(set) + eq(at_row[i], set) + end + + k = 0 + for i = 1,100 do + for j = 1,(10*size_factor) do + k = k + 1 + local ival = (j-1)*100+i + lib.marktree_del_pair_test(tree, ns, ids[ival]) + -- just a few stickprov, if there is trouble we need to check + -- everyone using the code in the "big tree" case above + if k % 100*size_factor == 0 or (k > 3000 and k % 200 == 0) then + check_intersections(tree) + end + end + end + + eq(0, tree[0].n_keys) + end) end) |