diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/config/paths.lua.in | 1 | ||||
-rw-r--r-- | test/functional/api/command_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/api/keymap_spec.lua | 12 | ||||
-rw-r--r-- | test/functional/api/vim_spec.lua | 19 | ||||
-rw-r--r-- | test/functional/api/window_spec.lua | 26 | ||||
-rw-r--r-- | test/functional/core/startup_spec.lua | 53 | ||||
-rw-r--r-- | test/functional/editor/meta_key_spec.lua | 15 | ||||
-rw-r--r-- | test/functional/fixtures/fake-lsp-server.lua | 33 | ||||
-rw-r--r-- | test/functional/fixtures/middle/filen.lua | 1 | ||||
-rw-r--r-- | test/functional/fixtures/pack/foo/opt/funky/filen.lua | 12 | ||||
-rw-r--r-- | test/functional/lua/diagnostic_spec.lua | 51 | ||||
-rw-r--r-- | test/functional/lua/json_spec.lua | 133 | ||||
-rw-r--r-- | test/functional/lua/ui_spec.lua | 46 | ||||
-rw-r--r-- | test/functional/plugin/lsp/codelens_spec.lua | 28 | ||||
-rw-r--r-- | test/functional/plugin/lsp_spec.lua | 192 | ||||
-rw-r--r-- | test/functional/ui/decorations_spec.lua | 508 | ||||
-rw-r--r-- | test/functional/ui/float_spec.lua | 316 | ||||
-rw-r--r-- | test/functional/ui/mouse_spec.lua | 124 |
18 files changed, 1385 insertions, 187 deletions
diff --git a/test/config/paths.lua.in b/test/config/paths.lua.in index 7fe5d8ad80..e3979981ba 100644 --- a/test/config/paths.lua.in +++ b/test/config/paths.lua.in @@ -19,5 +19,6 @@ if module.test_luajit_prg == '' then end end table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/include") +table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/src/nvim/auto") return module diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua index 37331d11c7..6f929ad1ca 100644 --- a/test/functional/api/command_spec.lua +++ b/test/functional/api/command_spec.lua @@ -21,7 +21,7 @@ describe('nvim_get_commands', function() it('validates input', function() eq('builtin=true not implemented', pcall_err(meths.get_commands, {builtin=true})) - eq('unexpected key: foo', pcall_err(meths.get_commands, + eq("Invalid key: 'foo'", pcall_err(meths.get_commands, {foo='blah'})) end) diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index 4194945645..dd8eef7ca0 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -436,16 +436,16 @@ describe('nvim_set_keymap, nvim_del_keymap', function() end) it('error on invalid optnames', function() - eq('Invalid key: silentt', + eq("Invalid key: 'silentt'", pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {silentt = true})) - eq('Invalid key: sidd', + eq("Invalid key: 'sidd'", pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {sidd = false})) - eq('Invalid key: nowaiT', + eq("Invalid key: 'nowaiT'", pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {nowaiT = false})) end) it('error on <buffer> option key', function() - eq('Invalid key: buffer', + eq("Invalid key: 'buffer'", pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {buffer = true})) end) @@ -454,8 +454,8 @@ describe('nvim_set_keymap, nvim_del_keymap', function() -- note: need '%' to escape hyphens, which have special meaning in lua it('throws an error when given non-boolean value for '..opt, function() local opts = {} - opts[opt] = 2 - eq('Gave non-boolean value for an opt: '..opt, + opts[opt] = 'fooo' + eq(opt..' is not a boolean', pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', opts)) end) end diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index ffef6a6066..6bcf8dd91f 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -12,10 +12,12 @@ local funcs = helpers.funcs local iswin = helpers.iswin local meths = helpers.meths local matches = helpers.matches +local mkdir_p = helpers.mkdir_p local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed local is_os = helpers.is_os local parse_context = helpers.parse_context local request = helpers.request +local rmdir = helpers.rmdir local source = helpers.source local next_msg = helpers.next_msg local tmpname = helpers.tmpname @@ -1117,7 +1119,7 @@ describe('API', function() describe('nvim_get_context', function() it('validates args', function() - eq('unexpected key: blah', + eq("Invalid key: 'blah'", pcall_err(nvim, 'get_context', {blah={}})) eq('invalid value for key: types', pcall_err(nvim, 'get_context', {types=42})) @@ -1574,6 +1576,18 @@ describe('API', function() end) describe('nvim_list_runtime_paths', function() + setup(function() + local pathsep = helpers.get_pathsep() + mkdir_p('Xtest'..pathsep..'a') + mkdir_p('Xtest'..pathsep..'b') + end) + teardown(function() + rmdir 'Xtest' + end) + before_each(function() + meths.set_current_dir 'Xtest' + end) + it('returns nothing with empty &runtimepath', function() meths.set_option('runtimepath', '') eq({}, meths.list_runtime_paths()) @@ -1601,8 +1615,7 @@ describe('API', function() local long_path = ('/a'):rep(8192) meths.set_option('runtimepath', long_path) local paths_list = meths.list_runtime_paths() - neq({long_path}, paths_list) - eq({long_path:sub(1, #(paths_list[1]))}, paths_list) + eq({}, paths_list) end) end) diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index c49d6405f4..11755a9d97 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -1,8 +1,9 @@ local helpers = require('test.functional.helpers')(after_each) local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq, - ok, feed, insert, eval = helpers.clear, helpers.nvim, helpers.curbuf, + ok, feed, insert, eval, tabpage = helpers.clear, helpers.nvim, helpers.curbuf, helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq, - helpers.neq, helpers.ok, helpers.feed, helpers.insert, helpers.eval + helpers.neq, helpers.ok, helpers.feed, helpers.insert, helpers.eval, + helpers.tabpage local poke_eventloop = helpers.poke_eventloop local curwinmeths = helpers.curwinmeths local funcs = helpers.funcs @@ -11,6 +12,7 @@ local NIL = helpers.NIL local meths = helpers.meths local command = helpers.command local pcall_err = helpers.pcall_err +local assert_alive = helpers.assert_alive -- check if str is visible at the beginning of some line local function is_visible(str) @@ -206,7 +208,7 @@ describe('API/win', function() end) end) - describe('{get,set}_option', function() + describe('nvim_win_get_option, nvim_win_set_option', function() it('works', function() curwin('set_option', 'colorcolumn', '4,3') eq('4,3', curwin('get_option', 'colorcolumn')) @@ -224,6 +226,18 @@ describe('API/win', function() pcall_err(curwin, 'get_option', 'statusline')) eq('', eval('&l:statusline')) -- confirm local value was not copied end) + + it('after switching windows #15390', function() + nvim('command', 'tabnew') + local tab1 = unpack(nvim('list_tabpages')) + local win1 = unpack(tabpage('list_wins', tab1)) + window('set_option', win1, 'statusline', 'window-status') + nvim('command', 'split') + nvim('command', 'wincmd J') + nvim('command', 'wincmd j') + eq('window-status', window('get_option', win1, 'statusline')) + assert_alive() + end) end) describe('get_position', function() @@ -354,13 +368,13 @@ describe('API/win', function() local win = meths.open_win(0, true, { relative='editor', row=10, col=10, width=50, height=10 }) - local tabpage = eval('tabpagenr()') + local tab = eval('tabpagenr()') command('tabprevious') eq(1, eval('tabpagenr()')) meths.win_close(win, false) - eq(1001, meths.tabpage_get_win(tabpage).id) - helpers.assert_alive() + eq(1001, meths.tabpage_get_win(tab).id) + assert_alive() end) end) diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index ff93f88a2d..bf2559f8d7 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -20,6 +20,7 @@ local retry = helpers.retry local rmdir = helpers.rmdir local sleep = helpers.sleep local iswin = helpers.iswin +local startswith = helpers.startswith local write_file = helpers.write_file local meths = helpers.meths @@ -310,7 +311,8 @@ describe('startup', function() end) local function pack_clear(cmd) - clear{args={'--cmd', 'set packpath=test/functional/fixtures', '--cmd', cmd}, env={XDG_CONFIG_HOME='test/functional/fixtures/'}} + -- add packages after config dir in rtp but before config/after + clear{args={'--cmd', 'set packpath=test/functional/fixtures', '--cmd', 'let paths=split(&rtp, ",")', '--cmd', 'let &rtp = paths[0]..",test/functional/fixtures,test/functional/fixtures/middle,"..join(paths[1:],",")', '--cmd', cmd}, env={XDG_CONFIG_HOME='test/functional/fixtures/'}} end @@ -351,12 +353,57 @@ describe('startup', function() it("handles the correct order with start packages and after/", function() pack_clear [[ lua _G.test_loadorder = {} vim.cmd "runtime! filen.lua" ]] - eq({'ordinary', 'FANCY', 'ordinary after', 'FANCY after'}, exec_lua [[ return _G.test_loadorder ]]) + eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + + it("handles the correct order with start packages and after/ after startup", function() + pack_clear [[ lua _G.test_loadorder = {} ]] + command [[ runtime! filen.lua ]] + eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + + it("handles the correct order with globpath(&rtp, ...)", function() + pack_clear [[ set loadplugins | lua _G.test_loadorder = {} ]] + command [[ + for x in globpath(&rtp, "filen.lua",1,1) + call v:lua.dofile(x) + endfor + ]] + eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + + local rtp = meths.get_option'rtp' + ok(startswith(rtp, 'test/functional/fixtures/nvim,test/functional/fixtures/pack/*/start/*,test/functional/fixtures/start/*,test/functional/fixtures,test/functional/fixtures/middle,'), 'rtp='..rtp) end) it("handles the correct order with opt packages and after/", function() pack_clear [[ lua _G.test_loadorder = {} vim.cmd "packadd! superspecial\nruntime! filen.lua" ]] - eq({'ordinary', 'SuperSpecial', 'FANCY', 'SuperSpecial after', 'ordinary after', 'FANCY after'}, exec_lua [[ return _G.test_loadorder ]]) + eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'FANCY after', 'SuperSpecial after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + + it("handles the correct order with opt packages and after/ after startup", function() + pack_clear [[ lua _G.test_loadorder = {} ]] + command [[ + packadd! superspecial + runtime! filen.lua + ]] + eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'FANCY after', 'SuperSpecial after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + + it("handles the correct order with opt packages and globpath(&rtp, ...)", function() + pack_clear [[ set loadplugins | lua _G.test_loadorder = {} ]] + command [[ + packadd! superspecial + for x in globpath(&rtp, "filen.lua",1,1) + call v:lua.dofile(x) + endfor + ]] + eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'SuperSpecial after', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + + it("handles the correct order with a package that changes packpath", function() + pack_clear [[ lua _G.test_loadorder = {} vim.cmd "packadd! funky\nruntime! filen.lua" ]] + eq({'ordinary', 'funky!', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + eq({'ordinary', 'funky!', 'mittel', 'ordinary after'}, exec_lua [[ return _G.nested_order ]]) end) end) diff --git a/test/functional/editor/meta_key_spec.lua b/test/functional/editor/meta_key_spec.lua index 2a9541ba96..c219204409 100644 --- a/test/functional/editor/meta_key_spec.lua +++ b/test/functional/editor/meta_key_spec.lua @@ -11,15 +11,20 @@ describe('meta-keys #8226 #13042', function() end) it('ALT/META, normal-mode', function() - -- Unmapped ALT-chords behave as ESC+c + -- Unmapped ALT-chord behaves as ESC+c. insert('hello') feed('0<A-x><M-x>') expect('llo') + -- Unmapped ALT-chord resolves isolated (non-ALT) ESC mapping. #13086 #15869 + command('nnoremap <ESC> A<lt>ESC><Esc>') + command('nnoremap ; A;<Esc>') + feed('<A-;><M-;>') + expect('llo<ESC>;<ESC>;') -- Mapped ALT-chord behaves as mapped. command('nnoremap <M-l> Ameta-l<Esc>') command('nnoremap <A-j> Aalt-j<Esc>') feed('<A-j><M-l>') - expect('lloalt-jmeta-l') + expect('llo<ESC>;<ESC>;alt-jmeta-l') end) it('ALT/META, visual-mode', function() @@ -27,11 +32,15 @@ describe('meta-keys #8226 #13042', function() insert('peaches') feed('viw<A-x>viw<M-x>') expect('peach') + -- Unmapped ALT-chord resolves isolated (non-ALT) ESC mapping. #13086 #15869 + command('vnoremap <ESC> A<lt>ESC>') + feed('viw<A-;><ESC>viw<M-;><ESC>') + expect('peach<ESC>;<ESC>;') -- Mapped ALT-chord behaves as mapped. command('vnoremap <M-l> Ameta-l<Esc>') command('vnoremap <A-j> Aalt-j<Esc>') feed('viw<A-j>viw<M-l>') - expect('peachalt-jmeta-l') + expect('peach<ESC>;<ESC>;alt-jmeta-l') end) it('ALT/META insert-mode', function() diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index 9579525502..8e03d9a46e 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -43,11 +43,11 @@ end local function read_message() local line = io.read("*l") local length = line:lower():match("content%-length:%s*(%d+)") - return vim.fn.json_decode(io.read(2 + length):sub(2)) + return vim.json.decode(io.read(2 + length):sub(2)) end local function send(payload) - io.stdout:write(format_message_with_content_length(vim.fn.json_encode(payload))) + io.stdout:write(format_message_with_content_length(vim.json.encode(payload))) end local function respond(id, err, result) @@ -564,6 +564,35 @@ function tests.decode_nil() } end + +function tests.code_action_with_resolve() + skeleton { + on_init = function() + return { + capabilities = { + codeActionProvider = { + resolveProvider = true + } + } + } + end; + body = function() + notify('start') + local cmd = { + title = 'Command 1', + command = 'dummy1' + } + expect_request('textDocument/codeAction', function() + return nil, { cmd, } + end) + expect_request('codeAction/resolve', function() + return nil, cmd + end) + notify('shutdown') + end; + } +end + -- Tests will be indexed by TEST_NAME local kill_timer = vim.loop.new_timer() diff --git a/test/functional/fixtures/middle/filen.lua b/test/functional/fixtures/middle/filen.lua new file mode 100644 index 0000000000..fce50cc776 --- /dev/null +++ b/test/functional/fixtures/middle/filen.lua @@ -0,0 +1 @@ +table.insert(_G.test_loadorder, "mittel") diff --git a/test/functional/fixtures/pack/foo/opt/funky/filen.lua b/test/functional/fixtures/pack/foo/opt/funky/filen.lua new file mode 100644 index 0000000000..a33b83c2a7 --- /dev/null +++ b/test/functional/fixtures/pack/foo/opt/funky/filen.lua @@ -0,0 +1,12 @@ +table.insert(_G.test_loadorder, "funky!") + +if not _G.nesty then + _G.nesty = true + local save_order = _G.test_loadorder + _G.test_loadorder = {} + _G.vim.o.pp = "" -- funky! + vim.cmd [[runtime! filen.lua ]] + _G.nested_order = _G.test_loadorder + _G.test_loadorder = save_order + _G.nesty = nil +end diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua index 9397af9d9f..45aa4915cd 100644 --- a/test/functional/lua/diagnostic_spec.lua +++ b/test/functional/lua/diagnostic_spec.lua @@ -874,6 +874,26 @@ describe('vim.diagnostic', function() return count_extmarks(diagnostic_bufnr, diagnostic_ns) ]]) end) + + it('sets signs', function() + local result = exec_lua [[ + vim.diagnostic.config({ + signs = true, + }) + + local diagnostics = { + make_error('Error', 1, 1, 1, 2), + make_warning('Warning', 3, 3, 3, 3), + } + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + + return vim.fn.sign_getplaced(diagnostic_bufnr, {group = '*'})[1].signs + ]] + + eq({2, 'DiagnosticSignError'}, {result[1].lnum, result[1].name}) + eq({4, 'DiagnosticSignWarn'}, {result[2].lnum, result[2].name}) + end) end) describe('show_line_diagnostics()', function() @@ -995,37 +1015,6 @@ describe('vim.diagnostic', function() end) end) - describe('set_signs()', function() - -- TODO(tjdevries): Find out why signs are not displayed when set from Lua...?? - pending('sets signs by default', function() - exec_lua [[ - vim.diagnostic.config({ - update_in_insert = true, - signs = true, - }) - - local diagnostics = { - make_error('Delayed Diagnostic', 1, 1, 1, 2), - make_error('Delayed Diagnostic', 3, 3, 3, 3), - } - - vim.api.nvim_win_set_buf(0, diagnostic_bufnr) - vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - - vim.diagnostic._set_signs(diagnostic_ns, diagnostic_bufnr, diagnostics) - -- return vim.fn.sign_getplaced() - ]] - - nvim("input", "o") - nvim("input", "<esc>") - - -- TODO(tjdevries): Find a way to get the signs to display in the test... - eq(nil, exec_lua [[ - return im.fn.sign_getplaced()[1].signs - ]]) - end) - end) - describe('setloclist()', function() it('sets diagnostics in lnum order', function() local loc_list = exec_lua [[ diff --git a/test/functional/lua/json_spec.lua b/test/functional/lua/json_spec.lua new file mode 100644 index 0000000000..fbb21bfd57 --- /dev/null +++ b/test/functional/lua/json_spec.lua @@ -0,0 +1,133 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local NIL = helpers.NIL +local exec_lua = helpers.exec_lua +local eq = helpers.eq + +describe('vim.json.decode function', function() + before_each(function() + clear() + end) + + it('parses null, true, false', function() + eq(NIL, exec_lua([[return vim.json.decode('null')]])) + eq(true, exec_lua([[return vim.json.decode('true')]])) + eq(false, exec_lua([[return vim.json.decode('false')]])) + end) + + it('parses integer numbers', function() + eq(100000, exec_lua([[return vim.json.decode('100000')]])) + eq(-100000, exec_lua([[return vim.json.decode('-100000')]])) + eq(100000, exec_lua([[return vim.json.decode(' 100000 ')]])) + eq(-100000, exec_lua([[return vim.json.decode(' -100000 ')]])) + eq(0, exec_lua([[return vim.json.decode('0')]])) + eq(0, exec_lua([[return vim.json.decode('-0')]])) + end) + + it('parses floating-point numbers', function() + -- This behavior differs from vim.fn.json_decode, which return '100000.0' + eq('100000', exec_lua([[return tostring(vim.json.decode('100000.0'))]])) + eq(100000.5, exec_lua([[return vim.json.decode('100000.5')]])) + eq(-100000.5, exec_lua([[return vim.json.decode('-100000.5')]])) + eq(-100000.5e50, exec_lua([[return vim.json.decode('-100000.5e50')]])) + eq(100000.5e50, exec_lua([[return vim.json.decode('100000.5e50')]])) + eq(100000.5e50, exec_lua([[return vim.json.decode('100000.5e+50')]])) + eq(-100000.5e-50, exec_lua([[return vim.json.decode('-100000.5e-50')]])) + eq(100000.5e-50, exec_lua([[return vim.json.decode('100000.5e-50')]])) + eq(100000e-50, exec_lua([[return vim.json.decode('100000e-50')]])) + eq(0.5, exec_lua([[return vim.json.decode('0.5')]])) + eq(0.005, exec_lua([[return vim.json.decode('0.005')]])) + eq(0.005, exec_lua([[return vim.json.decode('0.00500')]])) + eq(0.5, exec_lua([[return vim.json.decode('0.00500e+002')]])) + eq(0.00005, exec_lua([[return vim.json.decode('0.00500e-002')]])) + + eq(-0.0, exec_lua([[return vim.json.decode('-0.0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0.0e0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0.0e+0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0.0e-0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0e-0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0e-2')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0e+2')]])) + + eq(0.0, exec_lua([[return vim.json.decode('0.0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0.0e0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0.0e+0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0.0e-0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0e-0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0e-2')]])) + eq(0.0, exec_lua([[return vim.json.decode('0e+2')]])) + end) + + it('parses containers', function() + eq({1}, exec_lua([[return vim.json.decode('[1]')]])) + eq({NIL, 1}, exec_lua([[return vim.json.decode('[null, 1]')]])) + eq({['1']=2}, exec_lua([[return vim.json.decode('{"1": 2}')]])) + eq({['1']=2, ['3']={{['4']={['5']={{}, 1}}}}}, + exec_lua([[return vim.json.decode('{"1": 2, "3": [{"4": {"5": [ [], 1]}}]}')]])) + end) + + it('parses strings properly', function() + eq('\n', exec_lua([=[return vim.json.decode([["\n"]])]=])) + eq('', exec_lua([=[return vim.json.decode([[""]])]=])) + eq('\\/"\t\b\n\r\f', exec_lua([=[return vim.json.decode([["\\\/\"\t\b\n\r\f"]])]=])) + eq('/a', exec_lua([=[return vim.json.decode([["\/a"]])]=])) + -- Unicode characters: 2-byte, 3-byte + eq('«',exec_lua([=[return vim.json.decode([["«"]])]=])) + eq('ફ',exec_lua([=[return vim.json.decode([["ફ"]])]=])) + end) + + it('parses surrogate pairs properly', function() + eq('\240\144\128\128', exec_lua([[return vim.json.decode('"\\uD800\\uDC00"')]])) + end) + + it('accepts all spaces in every position where space may be put', function() + local s = ' \t\n\r \t\r\n \n\t\r \n\r\t \r\t\n \r\n\t\t \n\r\t \r\n\t\n \r\t\n\r \t\r \n\t\r\n \n \t\r\n \r\t\n\t \r\n\t\r \n\r \t\n\r\t \r \t\n\r \n\t\r\t \n\r\t\n \r\n \t\r\n\t' + local str = ('%s{%s"key"%s:%s[%s"val"%s,%s"val2"%s]%s,%s"key2"%s:%s1%s}%s'):gsub('%%s', s) + eq({key={'val', 'val2'}, key2=1}, exec_lua([[return vim.json.decode(...)]], str)) + end) + +end) + +describe('vim.json.encode function', function() + before_each(function() + clear() + end) + + it('dumps strings', function() + eq('"Test"', exec_lua([[return vim.json.encode('Test')]])) + eq('""', exec_lua([[return vim.json.encode('')]])) + eq('"\\t"', exec_lua([[return vim.json.encode('\t')]])) + eq('"\\n"', exec_lua([[return vim.json.encode('\n')]])) + -- vim.fn.json_encode return \\u001B + eq('"\\u001b"', exec_lua([[return vim.json.encode('\27')]])) + eq('"þÿþ"', exec_lua([[return vim.json.encode('þÿþ')]])) + end) + + it('dumps numbers', function() + eq('0', exec_lua([[return vim.json.encode(0)]])) + eq('10', exec_lua([[return vim.json.encode(10)]])) + eq('-10', exec_lua([[return vim.json.encode(-10)]])) + end) + + it('dumps floats', function() + eq('10.5', exec_lua([[return vim.json.encode(10.5)]])) + eq('-10.5', exec_lua([[return vim.json.encode(-10.5)]])) + eq('-1e-05', exec_lua([[return vim.json.encode(-1e-5)]])) + end) + + it('dumps lists', function() + eq('[]', exec_lua([[return vim.json.encode({})]])) + eq('[[]]', exec_lua([[return vim.json.encode({{}})]])) + eq('[[],[]]', exec_lua([[return vim.json.encode({{}, {}})]])) + end) + + it('dumps dictionaries', function() + eq('{}', exec_lua([[return vim.json.encode(vim.empty_dict())]])) + eq('{"d":[]}', exec_lua([[return vim.json.encode({d={}})]])) + end) + + it('dumps vim.NIL', function() + eq('null', exec_lua([[return vim.json.encode(vim.NIL)]])) + end) + +end) diff --git a/test/functional/lua/ui_spec.lua b/test/functional/lua/ui_spec.lua new file mode 100644 index 0000000000..94f1b5840b --- /dev/null +++ b/test/functional/lua/ui_spec.lua @@ -0,0 +1,46 @@ +local helpers = require('test.functional.helpers')(after_each) +local eq = helpers.eq +local exec_lua = helpers.exec_lua +local clear = helpers.clear + +describe('vim.ui', function() + before_each(function() + clear() + end) + + + describe('select', function() + it('can select an item', function() + local result = exec_lua[[ + local items = { + { name = 'Item 1' }, + { name = 'Item 2' }, + } + local opts = { + format_item = function(entry) + return entry.name + end + } + local selected + local cb = function(item) + selected = item + end + -- inputlist would require input and block the test; + local choices + vim.fn.inputlist = function(x) + choices = x + return 1 + end + vim.ui.select(items, opts, cb) + vim.wait(100, function() return selected ~= nil end) + return {selected, choices} + ]] + eq({ name = 'Item 1' }, result[1]) + eq({ + 'Select one of:', + '1: Item 1', + '2: Item 2', + }, result[2]) + end) + end) +end) diff --git a/test/functional/plugin/lsp/codelens_spec.lua b/test/functional/plugin/lsp/codelens_spec.lua index e48a0ad260..c8b75e65fc 100644 --- a/test/functional/plugin/lsp/codelens_spec.lua +++ b/test/functional/plugin/lsp/codelens_spec.lua @@ -58,5 +58,33 @@ describe('vim.lsp.codelens', function() ]], bufnr) eq({[1] = {'Lens1', 'LspCodeLens'}}, virtual_text_chunks) + + end) + it('codelens uses client commands', function() + local fake_uri = "file:///fake/uri" + local cmd = exec_lua([[ + fake_uri = ... + local bufnr = vim.uri_to_bufnr(fake_uri) + vim.fn.bufload(bufnr) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {'One line'}) + local lenses = { + { + range = { + start = { line = 0, character = 0, }, + ['end'] = { line = 0, character = 8 } + }, + command = { title = 'Lens1', command = 'Dummy' } + }, + } + vim.lsp.codelens.on_codelens(nil, lenses, {method='textDocument/codeLens', client_id=1, bufnr=bufnr}) + local cmd_called = nil + vim.lsp.commands['Dummy'] = function(command) + cmd_called = command + end + vim.api.nvim_set_current_buf(bufnr) + vim.lsp.codelens.run() + return cmd_called + ]], fake_uri) + eq({ command = 'Dummy', title = 'Lens1' }, cmd) end) end) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 27f2d2536f..8f9b194690 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -132,37 +132,38 @@ local function test_rpc_server(config) end describe('LSP', function() - describe('server_name specified', function() - before_each(function() - 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" - exec_lua([=[ - lsp = require('vim.lsp') - local test_name, fixture_filename, logfile = ... - function test__start_client() - return lsp.start_client { - cmd_env = { - NVIM_LOG_FILE = logfile; - }; - cmd = { - vim.v.progpath, '-Es', '-u', 'NONE', '--headless', - "-c", string.format("lua TEST_NAME = %q", test_name), - "-c", "luafile "..fixture_filename; - }; - root_dir = vim.loop.cwd(); - } - end - TEST_CLIENT1 = test__start_client() - ]=], test_name, fake_lsp_code, fake_lsp_logfile) - end) + before_each(function() + clear_notrace() - after_each(function() - exec_lua("lsp._vim_exit_handler()") - -- exec_lua("lsp.stop_all_clients(true)") - end) + -- Run an instance of nvim on the file which contains our "scripts". + -- Pass TEST_NAME to pick the script. + local test_name = "basic_init" + exec_lua([=[ + lsp = require('vim.lsp') + local test_name, fixture_filename, logfile = ... + function test__start_client() + return lsp.start_client { + cmd_env = { + NVIM_LOG_FILE = logfile; + }; + cmd = { + vim.v.progpath, '-Es', '-u', 'NONE', '--headless', + "-c", string.format("lua TEST_NAME = %q", test_name), + "-c", "luafile "..fixture_filename; + }; + root_dir = vim.loop.cwd(); + } + end + TEST_CLIENT1 = test__start_client() + ]=], test_name, fake_lsp_code, fake_lsp_logfile) + end) + after_each(function() + exec_lua("lsp._vim_exit_handler()") + -- exec_lua("lsp.stop_all_clients(true)") + end) + + describe('server_name specified', function() it('start_client(), stop_client()', function() retry(nil, 4000, function() eq(1, exec_lua('return #lsp.get_active_clients()')) @@ -334,7 +335,6 @@ describe('LSP', function() } end) it('workspace/configuration returns NIL per section if client was started without config.settings', function() - clear_notrace() fake_lsp_server_setup('workspace/configuration no settings') eq({ NIL, NIL, }, exec_lua [[ local result = { @@ -2022,83 +2022,6 @@ describe('LSP', function() end) end) - describe('lsp.util.make_floating_popup_options', function() - before_each(function() - exec_lua [[ - local bufnr = vim.uri_to_bufnr("file:///fake/uri") - local winheight = vim.fn.winheight(0) - for i = 1, winheight do - vim.api.nvim_buf_set_lines(bufnr, 0, 0, false, {''}) - end - vim.api.nvim_win_set_buf(0, bufnr) - vim.api.nvim_win_set_cursor(0, {winheight, 0}) - ]] - end) - - local function popup_row(opts) - return exec_lua([[ - return vim.lsp.util.make_floating_popup_options(...).row - ]], 2, 2, opts) - end - - local err_pattern = "^Error executing lua: %.%.%./util%.lua:0: invalid floating preview border: .*%. :help vim%.api%.nvim_open_win%(%)$" - - it('calculates default border height correctly', function() - eq(0, popup_row()) - end) - - it('calculates string border height correctly', function() - eq(0, popup_row({border = 'none'})) - eq(-2, popup_row({border = 'single'})) - eq(-2, popup_row({border = 'double'})) - eq(-2, popup_row({border = 'rounded'})) - eq(-2, popup_row({border = 'solid'})) - eq(-1, popup_row({border = 'shadow'})) - end) - - it('error on invalid string border', function() - matches(err_pattern, pcall_err(popup_row, {border = ''})) - matches(err_pattern, pcall_err(popup_row, {border = 'invalid'})) - end) - - it('error on invalid array border length', function() - matches(err_pattern, pcall_err(popup_row, {border = {}})) - matches(err_pattern, pcall_err(popup_row, {border = {'', '', ''}})) - matches(err_pattern, pcall_err(popup_row, {border = {'', '', '', '', ''}})) - end) - - it('error on invalid array border member type', function() - matches(err_pattern, pcall_err(popup_row, {border = {0}})) - end) - - it('calculates 8-array border height correctly', function() - eq(0, popup_row({border = {'', '', '', '', '', '', '', ''}})) - eq(-2, popup_row({border = {'', '~', '', '~', '', '~', '', '~'}})) - eq(-1, popup_row({border = {'', '', '', '~', '', '~', '', ''}})) - eq(0, popup_row({border = {'', '', '', {'~', 'NormalFloat'}, '', '', '', {'~', 'NormalFloat'}}})) - eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}, '', '', '', {'~', 'NormalFloat'}, '', ''}})) - end) - - it('calculates 4-array border height correctly', function() - eq(0, popup_row({border = {'', '', '', ''}})) - eq(-2, popup_row({border = {'', '~', '', '~'}})) - eq(0, popup_row({border = {'', '', '', {'~', 'NormalFloat'}}})) - eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}, '', ''}})) - end) - - it('calculates 2-array border height correctly', function() - eq(0, popup_row({border = {'', ''}})) - eq(-2, popup_row({border = {'', '~'}})) - eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}}})) - end) - - it('calculates 1-array border height correctly', function() - eq(0, popup_row({border = {''}})) - eq(-2, popup_row({border = {'~'}})) - eq(-2, popup_row({border = {{'~', 'NormalFloat'}}})) - end) - end) - describe('lsp.util._make_floating_popup_size', function() before_each(function() exec_lua [[ contents = @@ -2376,26 +2299,43 @@ describe('LSP', function() describe('vim.lsp.buf.code_action', function() it('Calls client side command if available', function() - eq(1, exec_lua [[ - local dummy_calls = 0 - vim.lsp.commands.dummy = function() - dummy_calls = dummy_calls + 1 - end - local actions = { - { - title = 'Dummy command', - command = 'dummy', - }, - } - -- inputlist would require input and block the test; - vim.fn.inputlist = function() - return 1 + local client + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; + } + test_rpc_server { + test_name = 'code_action_with_resolve', + on_init = function(client_) + client = client_ + end, + on_setup = function() + end, + on_exit = function(code, signal) + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) + end, + on_handler = function(err, result, ctx) + eq(table.remove(expected_handlers), {err, result, ctx}) + if ctx.method == 'start' then + exec_lua([[ + vim.lsp.commands['dummy1'] = function(cmd) + vim.lsp.commands['dummy2'] = function() + end + end + local bufnr = vim.api.nvim_get_current_buf() + vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID) + vim.fn.inputlist = function() + return 1 + end + vim.lsp.buf.code_action() + ]]) + elseif ctx.method == 'shutdown' then + eq('function', exec_lua[[return type(vim.lsp.commands['dummy2'])]]) + client.stop() + end end - local params = {} - local handler = require'vim.lsp.handlers'['textDocument/codeAction'] - handler(nil, actions, { method = 'textDocument/codeAction', params = params }, nil) - return dummy_calls - ]]) + } end) end) describe('vim.lsp.commands', function() diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 4373d17890..8074f91215 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -790,3 +790,511 @@ end]] helpers.assert_alive() end) end) + +describe('decorations: virtual lines', function() + local screen, ns + before_each(function() + clear() + screen = Screen.new(50, 12) + screen:attach() + screen:set_default_attr_ids { + [1] = {bold=true, foreground=Screen.colors.Blue}; + [2] = {foreground = Screen.colors.Cyan4}; + [3] = {background = Screen.colors.Yellow1}; + [4] = {bold = true}; + [5] = {background = Screen.colors.Yellow, foreground = Screen.colors.Blue}; + [6] = {foreground = Screen.colors.Blue}; + [7] = {foreground = Screen.colors.SlateBlue}; + [8] = {background = Screen.colors.WebGray, foreground = Screen.colors.DarkBlue}; + [9] = {foreground = Screen.colors.Brown}; + } + + ns = meths.create_namespace 'test' + end) + + local example_text = [[ +if (h->n_buckets < new_n_buckets) { // expand + khkey_t *new_keys = (khkey_t *)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); + h->keys = new_keys; + if (kh_is_map && val_size) { + char *new_vals = krealloc( h->vals_buf, new_n_buckets * val_size); + h->vals_buf = new_vals; + } +}]] + + it('works with one line', function() + insert(example_text) + feed 'gg' + meths.buf_set_extmark(0, ns, 1, 33, { + virt_lines={ {{">> ", "NonText"}, {"krealloc", "Identifier"}, {": change the size of an allocation"}}}; + virt_lines_above=true; + }) + + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + {1:>> }{2:krealloc}: change the size of an allocation | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + + feed '/krealloc<cr>' + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + {1:>> }{2:krealloc}: change the size of an allocation | + khkey_t *new_keys = (khkey_t *){3:^krealloc}((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + /krealloc | + ]]} + + -- virtual line remains anchored to the extmark + feed 'i<cr>' + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *) | + {1:>> }{2:krealloc}: change the size of an allocation | + {3:^krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + hkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + {4:-- INSERT --} | + ]]} + + feed '<esc>3+' + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *) | + {1:>> }{2:krealloc}: change the size of an allocation | + {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + hkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + ^char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + | + ]]} + + meths.buf_set_extmark(0, ns, 5, 0, { + virt_lines = { {{"^^ REVIEW:", "Todo"}, {" new_vals variable seems unneccesary?", "Comment"}} }; + }) + -- TODO: what about the cursor?? + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *) | + {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + hkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buck^ets * val_size); | + {5:^^ REVIEW:}{6: new_vals variable seems unneccesary?} | + h->vals_buf = new_vals; | + } | + | + ]]} + + meths.buf_clear_namespace(0, ns, 0, -1) + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *) | + {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + hkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buck^ets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + end) + + + it('works with text at the beginning of the buffer', function() + insert(example_text) + feed 'gg' + + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + {1:~ }| + | + ]]} + + meths.buf_set_extmark(0, ns, 0, 0, { + virt_lines={ + {{"refactor(khash): ", "Special"}, {"take size of values as parameter"}}; + {{"Author: Dev Devsson, "}, {"Tue Aug 31 10:13:37 2021", "Comment"}}; + }; + virt_lines_above=true; + right_gravity=false; + }) + + -- placing virt_text on topline does not automatically cause a scroll + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + {1:~ }| + | + ]], unchanged=true} + + feed '<c-b>' + screen:expect{grid=[[ + {7:refactor(khash): }take size of values as parameter | + Author: Dev Devsson, {6:Tue Aug 31 10:13:37 2021} | + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + | + ]]} + end) + + it('works with text et the end of the buffer', function() + insert(example_text) + feed 'G' + + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + ^} | + {1:~ }| + | + ]]} + + local id = meths.buf_set_extmark(0, ns, 7, 0, { + virt_lines={{{"Grugg"}}}; + right_gravity=false; + }) + + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + ^} | + Grugg | + | + ]]} + + meths.buf_del_extmark(0, ns, id) + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + ^} | + {1:~ }| + | + ]]} + end) + + it('works with a block scrolling up', function() + screen:try_resize(30, 7) + insert("aa\nbb\ncc\ndd\nee\nff\ngg\nhh") + feed 'gg' + + meths.buf_set_extmark(0, ns, 6, 0, { + virt_lines={ + {{"they see me"}}; + {{"scrolling", "Special"}}; + {{"they"}}; + {{"hatin'", "Special"}}; + }; + }) + + screen:expect{grid=[[ + ^aa | + bb | + cc | + dd | + ee | + ff | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^bb | + cc | + dd | + ee | + ff | + gg | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^cc | + dd | + ee | + ff | + gg | + they see me | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^dd | + ee | + ff | + gg | + they see me | + {7:scrolling} | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^ee | + ff | + gg | + they see me | + {7:scrolling} | + they | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^ff | + gg | + they see me | + {7:scrolling} | + they | + {7:hatin'} | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^gg | + they see me | + {7:scrolling} | + they | + {7:hatin'} | + hh | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + they see me | + {7:scrolling} | + they | + {7:hatin'} | + ^hh | + {1:~ }| + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + {7:scrolling} | + they | + {7:hatin'} | + ^hh | + {1:~ }| + {1:~ }| + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + they | + {7:hatin'} | + ^hh | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + {7:hatin'} | + ^hh | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^hh | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('works with sign and numbercolumns', function() + insert(example_text) + feed 'gg' + command 'set number signcolumn=yes' + screen:expect{grid=[[ + {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan| + {8: }{9: }d | + {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| + {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_| + {8: }{9: }t)); | + {8: }{9: 3 } h->keys = new_keys; | + {8: }{9: 4 } if (kh_is_map && val_size) { | + {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, | + {8: }{9: }new_n_buckets * val_size); | + {8: }{9: 6 } h->vals_buf = new_vals; | + {8: }{9: 7 } } | + | + ]]} + + meths.buf_set_extmark(0, ns, 2, 0, { + virt_lines={ + {{"Some special", "Special"}}; + {{"remark about codes", "Comment"}}; + }; + }) + + screen:expect{grid=[[ + {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan| + {8: }{9: }d | + {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| + {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_| + {8: }{9: }t)); | + {8: }{9: 3 } h->keys = new_keys; | + {8: }{9: }{7:Some special} | + {8: }{9: }{6:remark about codes} | + {8: }{9: 4 } if (kh_is_map && val_size) { | + {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, | + {8: }{9: }new_n_buckets * val_size); | + | + ]]} + + meths.buf_set_extmark(0, ns, 2, 0, { + virt_lines={ + {{"Some special", "Special"}}; + {{"remark about codes", "Comment"}}; + }; + virt_lines_leftcol=true; + }) + screen:expect{grid=[[ + {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan| + {8: }{9: }d | + {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| + {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_| + {8: }{9: }t)); | + {8: }{9: 3 } h->keys = new_keys; | + {7:Some special} | + {6:remark about codes} | + {8: }{9: 4 } if (kh_is_map && val_size) { | + {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, | + {8: }{9: }new_n_buckets * val_size); | + | + ]]} + end) + + + it('works with hard tabs', function() + insert(example_text) + feed 'gg' + meths.buf_set_extmark(0, ns, 1, 0, { + virt_lines={ {{">>", "NonText"}, {"\tvery\ttabby", "Identifier"}, {"text\twith\ttabs"}}}; + }) + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + {1:>>}{2: very tabby}text with tabs | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + + command 'set tabstop=4' + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + {1:>>}{2: very tabby}text with tabs | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + end) + +end) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index ccf5f963d1..e57c63bb0f 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -109,6 +109,21 @@ describe('float window', function() assert_alive() end) + it('closed immediately by autocmd after win_enter #15548', function() + eq('Error executing lua: [string "<nvim>"]:0: Window was closed immediately', + pcall_err(exec_lua, [[ + vim.cmd "autocmd BufLeave * ++once quit!" + local buf = vim.api.nvim_create_buf(true, true) + vim.api.nvim_open_win(buf, true, { + relative = "win", + row = 0, col = 0, + width = 1, height = 1, + noautocmd = false, + }) + ]])) + assert_alive() + end) + it('opened with correct height', function() local height = exec_lua([[ vim.api.nvim_set_option("winheight", 20) @@ -1514,7 +1529,7 @@ describe('float window', function() it('API has proper error messages', function() local buf = meths.create_buf(false,false) - eq("Invalid key 'bork'", + eq("Invalid key: 'bork'", pcall_err(meths.open_win,buf, false, {width=20,height=2,bork=true})) eq("'win' key is only valid with relative='win'", pcall_err(meths.open_win,buf, false, {width=20,height=2,relative='editor',row=0,col=0,win=0})) @@ -1527,13 +1542,15 @@ describe('float window', function() eq("'relative' requires 'row'/'col' or 'bufpos'", pcall_err(meths.open_win,buf, false, {width=20,height=2,relative='editor'})) eq("'width' key must be a positive Integer", - pcall_err(meths.open_win,buf, false, {width=-1,height=2,relative='editor'})) + pcall_err(meths.open_win,buf, false, {width=-1,height=2,relative='editor', row=0, col=0})) eq("'height' key must be a positive Integer", - pcall_err(meths.open_win,buf, false, {width=20,height=-1,relative='editor'})) + pcall_err(meths.open_win,buf, false, {width=20,height=-1,relative='editor', row=0, col=0})) eq("'height' key must be a positive Integer", - pcall_err(meths.open_win,buf, false, {width=20,height=0,relative='editor'})) - eq("Must specify 'width' and 'height'", - pcall_err(meths.open_win,buf, false, {relative='editor'})) + pcall_err(meths.open_win,buf, false, {width=20,height=0,relative='editor', row=0, col=0})) + eq("Must specify 'width'", + pcall_err(meths.open_win,buf, false, {relative='editor', row=0, col=0})) + eq("Must specify 'height'", + pcall_err(meths.open_win,buf, false, {relative='editor', row=0, col=0, width=2})) end) it('can be placed relative window or cursor', function() @@ -1866,6 +1883,293 @@ describe('float window', function() end end) + it('always anchor to corner including border', function() + screen:try_resize(40,13) + meths.buf_set_lines(0, 0, -1, true, {'just some example text', 'some more example text'}) + feed('ggeee') + command('below split') + if multigrid then + screen:expect([[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + just some exampl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ]]) + else + screen:expect([[ + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + {5:[No Name] [+] }| + just some exampl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + + local buf = meths.create_buf(false, false) + meths.buf_set_lines(buf, 0, -1, true, {' halloj! ', + ' BORDAA '}) + local win = meths.open_win(buf, false, {relative='cursor', width=9, height=2, row=1, col=-2, border="double"}) + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + just some exampl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 6 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═════════╝}| + ]], float_pos={ + [6] = {{id = 1003}, "NW", 4, 1, 14, true} + }} + else + screen:expect([[ + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + {5:[No Name] [+] }| + just some exampl^e text | + some more exam{5:╔═════════╗} | + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚═════════╝}{0: }| + {4:[No Name] [+] }| + | + ]]) + end + + meths.win_set_config(win, {relative='cursor', row=0, col=-2, anchor='NE'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + just some exampl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 6 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═════════╝}| + ]], float_pos={ + [6] = {{id = 1003}, "NE", 4, 0, 14, true} + }} + else + screen:expect([[ + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + {5:[No Name] [+] }| + jus{5:╔═════════╗}pl^e text | + som{5:║}{1: halloj! }{5:║}ple text | + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚═════════╝}{0: }| + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + + meths.win_set_config(win, {relative='cursor', row=1, col=-2, anchor='SE'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + just some exampl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 6 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═════════╝}| + ]], float_pos={ + [6] = {{id = 1003}, "SE", 4, 1, 14, true} + }} + else + screen:expect([[ + just some example text | + some more example text | + {0:~ }| + {0:~ }{5:╔═════════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {5:[No║}{1: BORDAA }{5:║ }| + jus{5:╚═════════╝}pl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + + meths.win_set_config(win, {relative='cursor', row=0, col=-2, anchor='SW'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + just some exampl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 6 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═════════╝}| + ]], float_pos={ + [6] = {{id = 1003}, "SW", 4, 0, 14, true} + }} + else + screen:expect([[ + just some example text | + some more example text | + {0:~ }{5:╔═════════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {5:[No Name] [+] ╚═════════╝ }| + just some exampl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + 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/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 7bca741ae3..d3fe38ef52 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -1384,4 +1384,128 @@ describe('ui/mouse/input', function() end) -- level 3 - wrapped end) + + it('getmousepos works correctly', function() + local winwidth = meths.get_option('winwidth') + -- Set winwidth=1 so that window sizes don't change. + meths.set_option('winwidth', 1) + command('tabedit') + local tabpage = meths.get_current_tabpage() + insert('hello') + command('vsplit') + local opts = { + relative='editor', + width=12, + height=1, + col=8, + row=1, + anchor='NW', + style='minimal', + border='single', + focusable=1 + } + local float = meths.open_win(meths.get_current_buf(), false, opts) + command('redraw') + local lines = meths.get_option('lines') + local columns = meths.get_option('columns') + + -- Test that screenrow and screencol are set properly for all positions. + for row = 0, lines - 1 do + for col = 0, columns - 1 do + -- Skip the X button that would close the tab. + if row ~= 0 or col ~= columns - 1 then + meths.input_mouse('left', 'press', '', 0, row, col) + meths.set_current_tabpage(tabpage) + local mousepos = funcs.getmousepos() + eq(row + 1, mousepos.screenrow) + eq(col + 1, mousepos.screencol) + -- All other values should be 0 when clicking on the command line. + if row == lines - 1 then + eq(0, mousepos.winid) + eq(0, mousepos.winrow) + eq(0, mousepos.wincol) + eq(0, mousepos.line) + eq(0, mousepos.column) + end + end + end + end + + -- Test that mouse position values are properly set for the floating window + -- with a border. 1 is added to the height and width to account for the + -- border. + for win_row = 0, opts.height + 1 do + for win_col = 0, opts.width + 1 do + local row = win_row + opts.row + local col = win_col + opts.col + meths.input_mouse('left', 'press', '', 0, row, col) + local mousepos = funcs.getmousepos() + eq(float.id, mousepos.winid) + eq(win_row + 1, mousepos.winrow) + eq(win_col + 1, mousepos.wincol) + local line = 0 + local column = 0 + if win_row > 0 and win_row < opts.height + 1 + and win_col > 0 and win_col < opts.width + 1 then + -- Because of border, win_row and win_col don't need to be + -- incremented by 1. + line = math.min(win_row, funcs.line('$')) + column = math.min(win_col, #funcs.getline(line) + 1) + end + eq(line, mousepos.line) + eq(column, mousepos.column) + end + end + + -- Test that mouse position values are properly set for the floating + -- window, after removing the border. + opts.border = 'none' + meths.win_set_config(float, opts) + command('redraw') + for win_row = 0, opts.height - 1 do + for win_col = 0, opts.width - 1 do + local row = win_row + opts.row + local col = win_col + opts.col + meths.input_mouse('left', 'press', '', 0, row, col) + local mousepos = funcs.getmousepos() + eq(float.id, mousepos.winid) + eq(win_row + 1, mousepos.winrow) + eq(win_col + 1, mousepos.wincol) + local line = math.min(win_row + 1, funcs.line('$')) + local column = math.min(win_col + 1, #funcs.getline(line) + 1) + eq(line, mousepos.line) + eq(column, mousepos.column) + end + end + + -- Test that mouse position values are properly set for ordinary windows. + -- Set the float to be unfocusable instead of closing, to additionally test + -- that getmousepos does not consider unfocusable floats. (see discussion + -- in PR #14937 for details). + opts.focusable = false + meths.win_set_config(float, opts) + command('redraw') + for nr = 1, 2 do + for win_row = 0, funcs.winheight(nr) - 1 do + for win_col = 0, funcs.winwidth(nr) - 1 do + local row = win_row + funcs.win_screenpos(nr)[1] - 1 + local col = win_col + funcs.win_screenpos(nr)[2] - 1 + meths.input_mouse('left', 'press', '', 0, row, col) + local mousepos = funcs.getmousepos() + eq(funcs.win_getid(nr), mousepos.winid) + eq(win_row + 1, mousepos.winrow) + eq(win_col + 1, mousepos.wincol) + local line = math.min(win_row + 1, funcs.line('$')) + local column = math.min(win_col + 1, #funcs.getline(line) + 1) + eq(line, mousepos.line) + eq(column, mousepos.column) + end + end + end + + -- Restore state and release mouse. + command('tabclose!') + meths.set_option('winwidth', winwidth) + meths.input_mouse('left', 'release', '', 0, 0, 0) + end) end) |