diff options
| author | Josh Rahm <joshuarahm@gmail.com> | 2024-05-24 19:18:11 +0000 |
|---|---|---|
| committer | Josh Rahm <joshuarahm@gmail.com> | 2024-05-24 19:18:11 +0000 |
| commit | ff7ed8f586589d620a806c3758fac4a47a8e7e15 (patch) | |
| tree | 729bbcb92231538fa61dab6c3d890b025484b7f5 /test/functional/plugin | |
| parent | 376914f419eb08fdf4c1a63a77e1f035898a0f10 (diff) | |
| parent | 28c04948a1c887a1cc0cb64de79fa32631700466 (diff) | |
| download | rneovim-ff7ed8f586589d620a806c3758fac4a47a8e7e15.tar.gz rneovim-ff7ed8f586589d620a806c3758fac4a47a8e7e15.tar.bz2 rneovim-ff7ed8f586589d620a806c3758fac4a47a8e7e15.zip | |
Merge remote-tracking branch 'upstream/master' into mix_20240309
Diffstat (limited to 'test/functional/plugin')
22 files changed, 1379 insertions, 573 deletions
diff --git a/test/functional/plugin/ccomplete_spec.lua b/test/functional/plugin/ccomplete_spec.lua index 903f16fc73..4c4c5e9444 100644 --- a/test/functional/plugin/ccomplete_spec.lua +++ b/test/functional/plugin/ccomplete_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed -local write_file = helpers.write_file +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local eval = n.eval +local feed = n.feed +local write_file = t.write_file describe('ccomplete#Complete', function() setup(function() diff --git a/test/functional/plugin/cfilter_spec.lua b/test/functional/plugin/cfilter_spec.lua index 37261d59df..220404a934 100644 --- a/test/functional/plugin/cfilter_spec.lua +++ b/test/functional/plugin/cfilter_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local fn = helpers.fn +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local fn = n.fn describe('cfilter.lua', function() before_each(function() diff --git a/test/functional/plugin/editorconfig_spec.lua b/test/functional/plugin/editorconfig_spec.lua index 115c28fbf6..839a723405 100644 --- a/test/functional/plugin/editorconfig_spec.lua +++ b/test/functional/plugin/editorconfig_spec.lua @@ -1,14 +1,18 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local pathsep = helpers.get_pathsep() -local fn = helpers.fn -local api = helpers.api -local exec_lua = helpers.exec_lua +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local pathsep = n.get_pathsep() +local fn = n.fn +local api = n.api +local exec_lua = n.exec_lua local testdir = 'Xtest-editorconfig' +--- @param name string +--- @param expected table<string,any> local function test_case(name, expected) local filename = testdir .. pathsep .. name command('edit ' .. filename) @@ -18,8 +22,8 @@ local function test_case(name, expected) end setup(function() - helpers.mkdir_p(testdir) - helpers.write_file( + n.mkdir_p(testdir) + t.write_file( testdir .. pathsep .. '.editorconfig', [[ root = true @@ -94,7 +98,7 @@ setup(function() end) teardown(function() - helpers.rmdir(testdir) + n.rmdir(testdir) end) describe('editorconfig', function() @@ -176,18 +180,18 @@ But not this one -- luacheck: pop local trimmed = untrimmed:gsub('%s+\n', '\n') - helpers.write_file(filename, untrimmed) + t.write_file(filename, untrimmed) command('edit ' .. filename) command('write') command('bdelete') - eq(trimmed, helpers.read_file(filename)) + eq(trimmed, t.read_file(filename)) filename = testdir .. pathsep .. 'no_trim.txt' - helpers.write_file(filename, untrimmed) + t.write_file(filename, untrimmed) command('edit ' .. filename) command('write') command('bdelete') - eq(untrimmed, helpers.read_file(filename)) + eq(untrimmed, t.read_file(filename)) end) it('sets textwidth', function() diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua index 8564ec7c9b..9c7c953fb0 100644 --- a/test/functional/plugin/health_spec.lua +++ b/test/functional/plugin/health_spec.lua @@ -1,14 +1,18 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local curbuf_contents = helpers.curbuf_contents -local command = helpers.command -local eq, neq, matches = helpers.eq, helpers.neq, helpers.matches -local getcompletion = helpers.fn.getcompletion -local insert = helpers.insert -local source = helpers.source -local exec_lua = helpers.exec_lua +local clear = n.clear +local curbuf_contents = n.curbuf_contents +local command = n.command +local eq, matches = t.eq, t.matches +local getcompletion = n.fn.getcompletion +local insert = n.insert +local exec_lua = n.exec_lua +local source = n.source +local assert_alive = n.assert_alive +local fn = n.fn +local api = n.api describe(':checkhealth', function() it('detects invalid $VIMRUNTIME', function() @@ -19,6 +23,7 @@ describe(':checkhealth', function() eq(false, status) eq('Invalid $VIMRUNTIME: bogus', string.match(err, 'Invalid.*')) end) + it("detects invalid 'runtimepath'", function() clear() command('set runtimepath=bogus') @@ -26,19 +31,29 @@ describe(':checkhealth', function() eq(false, status) eq("Invalid 'runtimepath'", string.match(err, 'Invalid.*')) end) + it('detects invalid $VIM', function() clear() -- Do this after startup, otherwise it just breaks $VIMRUNTIME. command("let $VIM='zub'") - command('checkhealth nvim') + command('checkhealth vim.health') matches('ERROR $VIM .* zub', curbuf_contents()) end) + it('completions can be listed via getcompletion()', function() clear() - eq('nvim', getcompletion('nvim', 'checkhealth')[1]) - eq('provider.clipboard', getcompletion('prov', 'checkhealth')[1]) + eq('vim.deprecated', getcompletion('vim', 'checkhealth')[1]) + eq('vim.provider', getcompletion('vim.prov', 'checkhealth')[1]) eq('vim.lsp', getcompletion('vim.ls', 'checkhealth')[1]) - neq('vim', getcompletion('^vim', 'checkhealth')[1]) -- should not complete vim.health + end) + + it('completion checks for vim.health._complete() return type #28456', function() + clear() + exec_lua([[vim.health._complete = function() return 1 end]]) + eq({}, getcompletion('', 'checkhealth')) + exec_lua([[vim.health._complete = function() return { 1 } end]]) + eq({}, getcompletion('', 'checkhealth')) + assert_alive() end) end) @@ -52,7 +67,7 @@ describe('health.vim', function() describe(':checkhealth', function() it('functions report_*() render correctly', function() command('checkhealth full_render') - helpers.expect([[ + n.expect([[ ============================================================================== test_plug.full_render: require("test_plug.full_render.health").check() @@ -75,7 +90,7 @@ describe('health.vim', function() it('concatenates multiple reports', function() command('checkhealth success1 success2 test_plug') - helpers.expect([[ + n.expect([[ ============================================================================== test_plug: require("test_plug.health").check() @@ -105,7 +120,7 @@ describe('health.vim', function() it('lua plugins submodules', function() command('checkhealth test_plug.submodule') - helpers.expect([[ + n.expect([[ ============================================================================== test_plug.submodule: require("test_plug.submodule.health").check() @@ -120,7 +135,7 @@ describe('health.vim', function() it('... including empty reports', function() command('checkhealth test_plug.submodule_empty') - helpers.expect([[ + n.expect([[ ============================================================================== test_plug.submodule_empty: require("test_plug.submodule_empty.health").check() @@ -158,28 +173,10 @@ describe('health.vim', function() } end) - it('fold healthchecks', function() - local screen = Screen.new(50, 7) - screen:attach() - command('checkhealth foo success1') - command('set nowrap laststatus=0') - screen:expect { - grid = [[ - ^ | - ──────────────────────────────────────────────────| - +WE 4 lines: foo: ·······························| - ──────────────────────────────────────────────────| - +-- 8 lines: test_plug.success1: require("test_pl| - ~ | - | - ]], - } - end) - it('gracefully handles invalid healthcheck', function() command('checkhealth non_existent_healthcheck') -- luacheck: ignore 613 - helpers.expect([[ + n.expect([[ ============================================================================== non_existent_healthcheck: @@ -191,7 +188,7 @@ describe('health.vim', function() it('does not use vim.health as a healtcheck', function() -- vim.health is not a healthcheck command('checkhealth vim') - helpers.expect([[ + n.expect([[ ERROR: No healthchecks found.]]) end) end) @@ -218,6 +215,12 @@ describe(':checkhealth window', function() it('opens directly if no buffer created', function() local screen = Screen.new(50, 12) + screen:set_default_attr_ids { + [1] = { foreground = Screen.colors.Blue, bold = true }, + [14] = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray }, + [31] = { foreground = tonumber('0x6a0dad') }, + [32] = { foreground = Screen.colors.PaleGreen2 }, + } screen:attach({ ext_multigrid = true }) command('checkhealth success1') screen:expect { @@ -227,16 +230,16 @@ describe(':checkhealth window', function() [3:--------------------------------------------------]| ## grid 2 ^ | - ──────────────────────────────────────────────────| - ──────────────────────────── | - test_plug.success1: require("test_plug.success1. | - health").check() | + {14:──────────────────────────────────────────────────}| + {14:────────────────────────────} | + {31:test_plug.success1: require("test_plug.success1. }| + {31:health").check()} | | - report 1 | - - OK everything is fine | + {31:report 1} | + - {32:OK} everything is fine | | - report 2 | - - OK nothing to see here | + {31:report 2} | + - {32:OK} nothing to see here | ## grid 3 | ]], @@ -245,6 +248,12 @@ describe(':checkhealth window', function() local function test_health_vsplit(left, emptybuf, mods) local screen = Screen.new(50, 20) + screen:set_default_attr_ids { + [1] = { foreground = Screen.colors.Blue, bold = true }, + [14] = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray }, + [31] = { foreground = tonumber('0x6a0dad') }, + [32] = { foreground = Screen.colors.PaleGreen2 }, + } screen:attach({ ext_multigrid = true }) if not emptybuf then insert('hello') @@ -257,24 +266,24 @@ describe(':checkhealth window', function() [3:--------------------------------------------------]| ## grid 2 %s | - ~ |*18 + {1:~ }|*18 ## grid 3 | ## grid 4 ^ | - ─────────────────────────|*3 - ─── | - test_plug.success1: | - require("test_plug. | - success1.health").check()| + {14:─────────────────────────}|*3 + {14:───} | + {31:test_plug.success1: }| + {31:require("test_plug. }| + {31:success1.health").check()}| | - report 1 | - - OK everything is fine | + {31:report 1} | + - {32:OK} everything is fine | | - report 2 | - - OK nothing to see here | + {31:report 2} | + - {32:OK} nothing to see here | | - ~ |*4 + {1:~ }|*4 ]]):format( left and '[4:-------------------------]│[2:------------------------]|*19' or '[2:------------------------]│[4:-------------------------]|*19', @@ -304,6 +313,7 @@ describe(':checkhealth window', function() local function test_health_split(top, emptybuf, mods) local screen = Screen.new(50, 25) screen:attach({ ext_multigrid = true }) + screen._default_attr_ids = nil if not emptybuf then insert('hello') end @@ -368,7 +378,7 @@ describe(':checkhealth window', function() it('opens in tab', function() -- create an empty buffer called "my_buff" - exec_lua 'vim.api.nvim_create_buf(false, true)' + api.nvim_create_buf(false, true) command('file my_buff') command('checkhealth success1') -- define a function that collects all buffers in each tab @@ -387,7 +397,7 @@ describe(':checkhealth window', function() return buffs endfunction ]]) - local buffers_per_tab = exec_lua('return vim.fn.CollectBuffersPerTab()') + local buffers_per_tab = fn.CollectBuffersPerTab() eq(buffers_per_tab, { tab1 = { 'my_buff' }, tab2 = { 'health://' } }) end) end) diff --git a/test/functional/plugin/lsp/codelens_spec.lua b/test/functional/plugin/lsp/codelens_spec.lua index 29daf7a066..cd20e95dd1 100644 --- a/test/functional/plugin/lsp/codelens_spec.lua +++ b/test/functional/plugin/lsp/codelens_spec.lua @@ -1,14 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local exec_lua = helpers.exec_lua -local eq = helpers.eq +local exec_lua = n.exec_lua +local eq = t.eq describe('vim.lsp.codelens', function() before_each(function() - helpers.clear() + n.clear() exec_lua('require("vim.lsp")') end) - after_each(helpers.clear) + after_each(n.clear) it('on_codelens_stores_and_displays_lenses', function() local fake_uri = 'file:///fake/uri' diff --git a/test/functional/plugin/lsp/completion_spec.lua b/test/functional/plugin/lsp/completion_spec.lua index 655eb76be6..2798d57381 100644 --- a/test/functional/plugin/lsp/completion_spec.lua +++ b/test/functional/plugin/lsp/completion_spec.lua @@ -1,7 +1,9 @@ ---@diagnostic disable: no-unknown -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq -local exec_lua = helpers.exec_lua +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq = t.eq +local exec_lua = n.exec_lua --- Convert completion results. --- @@ -41,7 +43,7 @@ local function complete(line, candidates, lnum) end describe('vim.lsp._completion', function() - before_each(helpers.clear) + before_each(n.clear) -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion it('prefers textEdit over label as word', function() diff --git a/test/functional/plugin/lsp/diagnostic_spec.lua b/test/functional/plugin/lsp/diagnostic_spec.lua index 705c182df7..c5e14ffdc2 100644 --- a/test/functional/plugin/lsp/diagnostic_spec.lua +++ b/test/functional/plugin/lsp/diagnostic_spec.lua @@ -1,12 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) -local lsp_helpers = require('test.functional.plugin.lsp.helpers') +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local neq = require('test.helpers').neq +local t_lsp = require('test.functional.plugin.lsp.testutil') -local create_server_definition = lsp_helpers.create_server_definition +local clear = n.clear +local exec_lua = n.exec_lua +local eq = t.eq +local neq = t.neq + +local create_server_definition = t_lsp.create_server_definition describe('vim.lsp.diagnostic', function() local fake_uri @@ -257,7 +259,7 @@ describe('vim.lsp.diagnostic', function() }, {client_id=client_id}) return vim.fn.bufnr(vim.uri_to_fname("file:///fake/uri2")) ]] - eq(bufnr, -1) + eq(-1, bufnr) -- Create buffer on diagnostics bufnr = exec_lua [[ @@ -269,8 +271,8 @@ describe('vim.lsp.diagnostic', function() }, {client_id=client_id}) return vim.fn.bufnr(vim.uri_to_fname("file:///fake/uri2")) ]] - neq(bufnr, -1) - eq(exec_lua([[return #vim.diagnostic.get(...)]], bufnr), 1) + neq(-1, bufnr) + eq(1, exec_lua([[return #vim.diagnostic.get(...)]], bufnr)) -- Clear diagnostics after buffer was created bufnr = exec_lua [[ @@ -280,8 +282,8 @@ describe('vim.lsp.diagnostic', function() }, {client_id=client_id}) return vim.fn.bufnr(vim.uri_to_fname("file:///fake/uri2")) ]] - neq(bufnr, -1) - eq(exec_lua([[return #vim.diagnostic.get(...)]], bufnr), 0) + neq(-1, bufnr) + eq(0, exec_lua([[return #vim.diagnostic.get(...)]], bufnr)) end) end) diff --git a/test/functional/plugin/lsp/handler_spec.lua b/test/functional/plugin/lsp/handler_spec.lua index 56e29e7337..013a5fb5e7 100644 --- a/test/functional/plugin/lsp/handler_spec.lua +++ b/test/functional/plugin/lsp/handler_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local pcall_err = helpers.pcall_err -local matches = helpers.matches +local eq = t.eq +local exec_lua = n.exec_lua +local pcall_err = t.pcall_err +local matches = t.matches describe('lsp-handlers', function() describe('vim.lsp._with_extend', function() diff --git a/test/functional/plugin/lsp/incremental_sync_spec.lua b/test/functional/plugin/lsp/incremental_sync_spec.lua index bd1842ceb5..238b90b57d 100644 --- a/test/functional/plugin/lsp/incremental_sync_spec.lua +++ b/test/functional/plugin/lsp/incremental_sync_spec.lua @@ -1,11 +1,12 @@ -- Test suite for testing interactions with the incremental sync algorithms powering the LSP client -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local api = helpers.api -local clear = helpers.clear -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local feed = helpers.feed +local api = n.api +local clear = n.clear +local eq = t.eq +local exec_lua = n.exec_lua +local feed = n.feed before_each(function() clear() diff --git a/test/functional/plugin/lsp/inlay_hint_spec.lua b/test/functional/plugin/lsp/inlay_hint_spec.lua index 192797b312..d3b5ae0e4e 100644 --- a/test/functional/plugin/lsp/inlay_hint_spec.lua +++ b/test/functional/plugin/lsp/inlay_hint_spec.lua @@ -1,14 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) -local lsp_helpers = require('test.functional.plugin.lsp.helpers') +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') +local t_lsp = require('test.functional.plugin.lsp.testutil') -local eq = helpers.eq -local dedent = helpers.dedent -local exec_lua = helpers.exec_lua -local insert = helpers.insert +local eq = t.eq +local dedent = t.dedent +local exec_lua = n.exec_lua +local insert = n.insert +local api = n.api -local clear_notrace = lsp_helpers.clear_notrace -local create_server_definition = lsp_helpers.create_server_definition +local clear_notrace = t_lsp.clear_notrace +local create_server_definition = t_lsp.create_server_definition local text = dedent([[ auto add(int a, int b) { return a + b; } @@ -41,12 +43,12 @@ local grid_without_inlay_hints = [[ ]] local grid_with_inlay_hints = [[ - auto add(int a, int b)-> int { return a + b; } | + auto add(int a, int b){1:-> int} { return a + b; } | | int main() { | int x = 1; | int y = 2; | - return add(a: x,b: y); | + return add({1:a:} x,{1:b:} y); | } | ^} | | @@ -68,8 +70,8 @@ before_each(function() inlayHintProvider = true, }, handlers = { - ['textDocument/inlayHint'] = function() - return vim.json.decode(response) + ['textDocument/inlayHint'] = function(_, _, callback) + callback(nil, vim.json.decode(response)) end, } }) @@ -83,12 +85,12 @@ before_each(function() ) insert(text) - exec_lua([[vim.lsp.inlay_hint.enable(bufnr)]]) + exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]]) screen:expect({ grid = grid_with_inlay_hints }) end) after_each(function() - exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })") + api.nvim_exec_autocmds('VimLeavePre', { modeline = false }) end) describe('vim.lsp.inlay_hint', function() @@ -104,13 +106,13 @@ describe('vim.lsp.inlay_hint', function() inlayHintProvider = true, }, handlers = { - ['textDocument/inlayHint'] = function() - return {} + ['textDocument/inlayHint'] = function(_, _, callback) + callback(nil, {}) end, } }) client2 = vim.lsp.start({ name = 'dummy2', cmd = server2.cmd }) - vim.lsp.inlay_hint.enable(bufnr) + vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) ]]) exec_lua([[ vim.lsp.stop_client(client2) ]]) @@ -118,18 +120,66 @@ describe('vim.lsp.inlay_hint', function() end) describe('enable()', function() - it('clears/applies inlay hints when passed false/true/nil', function() - exec_lua([[vim.lsp.inlay_hint.enable(bufnr, false)]]) - screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) + it('validation', function() + t.matches( + 'enable: expected boolean, got table', + t.pcall_err(exec_lua, [[vim.lsp.inlay_hint.enable({}, { bufnr = bufnr })]]) + ) + t.matches( + 'enable: expected boolean, got number', + t.pcall_err(exec_lua, [[vim.lsp.inlay_hint.enable(42)]]) + ) + t.matches( + 'filter: expected table, got number', + t.pcall_err(exec_lua, [[vim.lsp.inlay_hint.enable(true, 42)]]) + ) + end) + + describe('clears/applies inlay hints when passed false/true/nil', function() + before_each(function() + exec_lua([[ + bufnr2 = vim.api.nvim_create_buf(true, false) + vim.lsp.buf_attach_client(bufnr2, client_id) + vim.api.nvim_win_set_buf(0, bufnr2) + ]]) + insert(text) + exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr2 })]]) + exec_lua([[vim.api.nvim_win_set_buf(0, bufnr)]]) + screen:expect({ grid = grid_with_inlay_hints }) + end) + + it('for one single buffer', function() + exec_lua([[ + vim.lsp.inlay_hint.enable(false, { bufnr = bufnr }) + vim.api.nvim_win_set_buf(0, bufnr2) + ]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + exec_lua([[vim.api.nvim_win_set_buf(0, bufnr)]]) + screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) - exec_lua([[vim.lsp.inlay_hint.enable(bufnr, true)]]) - screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) - exec_lua([[vim.lsp.inlay_hint.enable(bufnr, not vim.lsp.inlay_hint.is_enabled(bufnr))]]) - screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) + exec_lua( + [[vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled({ bufnr = bufnr }), { bufnr = bufnr })]] + ) + screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) - exec_lua([[vim.lsp.inlay_hint.enable(bufnr)]]) - screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + end) + + it('for all buffers', function() + exec_lua([[vim.lsp.inlay_hint.enable(false)]]) + screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) + exec_lua([[vim.api.nvim_win_set_buf(0, bufnr2)]]) + screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) + + exec_lua([[vim.lsp.inlay_hint.enable(true)]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + exec_lua([[vim.api.nvim_win_set_buf(0, bufnr)]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + end) end) end) @@ -156,25 +206,25 @@ describe('vim.lsp.inlay_hint', function() inlayHintProvider = true, }, handlers = { - ['textDocument/inlayHint'] = function() - return { expected2 } + ['textDocument/inlayHint'] = function(_, _, callback) + callback(nil, { expected2 }) end, } }) client2 = vim.lsp.start({ name = 'dummy2', cmd = server2.cmd }) - vim.lsp.inlay_hint.enable(bufnr) + vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) ]], expected2 ) --- @type vim.lsp.inlay_hint.get.ret local res = exec_lua([[return vim.lsp.inlay_hint.get()]]) - eq(res, { + eq({ { bufnr = 1, client_id = 1, inlay_hint = expected[1] }, { bufnr = 1, client_id = 1, inlay_hint = expected[2] }, { bufnr = 1, client_id = 1, inlay_hint = expected[3] }, { bufnr = 1, client_id = 2, inlay_hint = expected2 }, - }) + }, res) --- @type vim.lsp.inlay_hint.get.ret res = exec_lua([[return vim.lsp.inlay_hint.get({ @@ -183,9 +233,9 @@ describe('vim.lsp.inlay_hint', function() ["end"] = { line = 2, character = 10 }, }, })]]) - eq(res, { + eq({ { bufnr = 1, client_id = 2, inlay_hint = expected2 }, - }) + }, res) --- @type vim.lsp.inlay_hint.get.ret res = exec_lua([[return vim.lsp.inlay_hint.get({ @@ -195,16 +245,16 @@ describe('vim.lsp.inlay_hint', function() ["end"] = { line = 5, character = 17 }, }, })]]) - eq(res, { + eq({ { bufnr = 1, client_id = 1, inlay_hint = expected[2] }, { bufnr = 1, client_id = 1, inlay_hint = expected[3] }, - }) + }, res) --- @type vim.lsp.inlay_hint.get.ret res = exec_lua([[return vim.lsp.inlay_hint.get({ bufnr = vim.api.nvim_get_current_buf() + 1, })]]) - eq(res, {}) + eq({}, res) end) end) end) diff --git a/test/functional/plugin/lsp/semantic_tokens_spec.lua b/test/functional/plugin/lsp/semantic_tokens_spec.lua index 77e39c81c8..7908c5d2e7 100644 --- a/test/functional/plugin/lsp/semantic_tokens_spec.lua +++ b/test/functional/plugin/lsp/semantic_tokens_spec.lua @@ -1,25 +1,27 @@ -local helpers = require('test.functional.helpers')(after_each) -local lsp_helpers = require('test.functional.plugin.lsp.helpers') +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') +local t_lsp = require('test.functional.plugin.lsp.testutil') -local command = helpers.command -local dedent = helpers.dedent -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local feed = helpers.feed -local feed_command = helpers.feed_command -local insert = helpers.insert -local matches = helpers.matches +local command = n.command +local dedent = t.dedent +local eq = t.eq +local exec_lua = n.exec_lua +local feed = n.feed +local feed_command = n.feed_command +local insert = n.insert +local matches = t.matches +local api = n.api -local clear_notrace = lsp_helpers.clear_notrace -local create_server_definition = lsp_helpers.create_server_definition +local clear_notrace = t_lsp.clear_notrace +local create_server_definition = t_lsp.create_server_definition before_each(function() clear_notrace() end) after_each(function() - exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })") + api.nvim_exec_autocmds('VimLeavePre', { modeline = false }) end) describe('semantic token highlighting', function() @@ -37,6 +39,8 @@ describe('semantic token highlighting', function() [7] = { bold = true, foreground = Screen.colors.DarkCyan }, [8] = { bold = true, foreground = Screen.colors.SlateBlue }, [9] = { bold = true, foreground = tonumber('0x6a0dad') }, + [10] = { bold = true, foreground = Screen.colors.Brown }, + [11] = { foreground = Screen.colors.Magenta1 }, } command([[ hi link @lsp.type.namespace Type ]]) command([[ hi link @lsp.type.function Special ]]) @@ -91,11 +95,11 @@ describe('semantic token highlighting', function() }, }, handlers = { - ['textDocument/semanticTokens/full'] = function() - return vim.fn.json_decode(response) + ['textDocument/semanticTokens/full'] = function(_, _, callback) + callback(nil, vim.fn.json_decode(response)) end, - ['textDocument/semanticTokens/full/delta'] = function() - return vim.fn.json_decode(edit_response) + ['textDocument/semanticTokens/full/delta'] = function(_, _, callback) + callback(nil, vim.fn.json_decode(edit_response)) end, } }) @@ -269,6 +273,63 @@ describe('semantic token highlighting', function() end ) + it('highlights start and stop when using "0" for current buffer', function() + exec_lua([[ + bufnr = vim.api.nvim_get_current_buf() + vim.api.nvim_win_set_buf(0, bufnr) + client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + ]]) + + insert(text) + + exec_lua([[ + vim.notify = function() end + vim.lsp.semantic_tokens.stop(0, client_id) + ]]) + + screen:expect { + grid = [[ + #include <iostream> | + | + int main() | + { | + int x; | + #ifdef __cplusplus | + std::cout << x << "\n"; | + #else | + printf("%d\n", x); | + #endif | + } | + ^} | + {1:~ }|*3 + | + ]], + } + + exec_lua([[ + vim.lsp.semantic_tokens.start(0, client_id) + ]]) + + screen:expect { + grid = [[ + #include <iostream> | + | + int {8:main}() | + { | + int {7:x}; | + #ifdef {5:__cplusplus} | + {4:std}::{2:cout} << {2:x} << "\n"; | + {6:#else} | + {6: printf("%d\n", x);} | + {6:#endif} | + } | + ^} | + {1:~ }|*3 + | + ]], + } + end) + it('buffer is re-highlighted when force refreshed', function() exec_lua([[ bufnr = vim.api.nvim_get_current_buf() @@ -499,11 +560,11 @@ describe('semantic token highlighting', function() }, }, handlers = { - ['textDocument/semanticTokens/full'] = function() - return nil + ['textDocument/semanticTokens/full'] = function(_, _, callback) + callback(nil, nil) end, ['textDocument/semanticTokens/full/delta'] = function() - return nil + callback(nil, nil) end, } }) @@ -547,11 +608,11 @@ describe('semantic token highlighting', function() }, }, handlers = { - ['textDocument/semanticTokens/full'] = function() - return vim.fn.json_decode(response) + ['textDocument/semanticTokens/full'] = function(_, _, callback) + callback(nil, vim.fn.json_decode(response)) end, - ['textDocument/semanticTokens/full/delta'] = function() - return vim.fn.json_decode(edit_response) + ['textDocument/semanticTokens/full/delta'] = function(_, _, callback) + callback(nil, vim.fn.json_decode(edit_response)) end, } }) @@ -837,11 +898,13 @@ b = "as"]], { it = 'rust-analyzer', text = [[pub fn main() { - break rust; - /// what? + println!("Hello world!"); + break rust; + /// what? } ]], - response = [[{"data": [0, 0, 3, 1, 0, 0, 4, 2, 1, 0, 0, 3, 4, 14, 524290, 0, 4, 1, 45, 0, 0, 1, 1, 45, 0, 0, 2, 1, 26, 0, 1, 4, 5, 1, 8192, 0, 6, 4, 52, 0, 0, 4, 1, 48, 0, 1, 4, 9, 0, 1, 1, 0, 1, 26, 0], "resultId": "1"}]], + response = [[{"data": [0, 0, 3, 1, 0, 0, 4, 2, 1, 0, 0, 3, 4, 14, 524290, 0, 4, 1, 45, 0, 0, 1, 1, 45, 0, 0, 2, 1, 26, 0, 1, 4, 8, 17, 0, 0, 8, 1, 45, 0, 0, 1, 14, 2, 0, 0, 14, 1, 45, 0, 0, 1, 1, 48, 0, 1, 4, 5, 1, 8192, 0, 6, 4, 52, 0, 0, 4, 1, 48, 0, 1, 4, 9, 0, 1, 1, 0, 1, 26, 0 ], "resultId": "1"}]], + legend = [[{ "tokenTypes": [ "comment", "keyword", "string", "number", "regexp", "operator", "namespace", "type", "struct", "class", "interface", "enum", "enumMember", "typeParameter", "function", "method", "property", "macro", "variable", @@ -904,6 +967,46 @@ b = "as"]], }, { line = 1, + modifiers = {}, + start_col = 4, + end_col = 12, + type = 'macro', -- println! + marked = true, + }, + { + line = 1, + modifiers = {}, + start_col = 12, + end_col = 13, + type = 'parenthesis', + marked = true, + }, + { + line = 1, + modifiers = {}, + start_col = 13, + end_col = 27, + type = 'string', -- "Hello world!" + marked = true, + }, + { + line = 1, + modifiers = {}, + start_col = 27, + end_col = 28, + type = 'parenthesis', + marked = true, + }, + { + line = 1, + modifiers = {}, + start_col = 28, + end_col = 29, + type = 'semicolon', + marked = true, + }, + { + line = 2, modifiers = { controlFlow = true }, start_col = 4, end_col = 9, -- break @@ -911,31 +1014,31 @@ b = "as"]], marked = true, }, { - line = 1, + line = 2, modifiers = {}, start_col = 10, - end_col = 13, -- rust + end_col = 14, -- rust type = 'unresolvedReference', marked = true, }, { - line = 1, + line = 2, modifiers = {}, - start_col = 13, - end_col = 13, + start_col = 14, + end_col = 15, type = 'semicolon', marked = true, }, { - line = 2, + line = 3, modifiers = { documentation = true }, start_col = 4, - end_col = 11, + end_col = 13, type = 'comment', -- /// what? marked = true, }, { - line = 3, + line = 4, modifiers = {}, start_col = 0, end_col = 1, @@ -946,12 +1049,13 @@ b = "as"]], expected_screen = function() screen:expect { grid = [[ - pub fn {8:main}() { | - break rust; | - //{6:/ what?} | + {10:pub} {10:fn} {8:main}() { | + {5:println!}({11:"Hello world!"}); | + {10:break} rust; | + {6:/// what?} | } | ^ | - {1:~ }|*10 + {1:~ }|*9 | ]], } @@ -971,8 +1075,8 @@ b = "as"]], }, }, handlers = { - ['textDocument/semanticTokens/full'] = function() - return vim.fn.json_decode(resp) + ['textDocument/semanticTokens/full'] = function(_, _, callback) + callback(nil, vim.fn.json_decode(resp)) end, } }) @@ -1357,11 +1461,11 @@ int main() }, }, handlers = { - ['textDocument/semanticTokens/full'] = function() - return vim.fn.json_decode(resp1) + ['textDocument/semanticTokens/full'] = function(_, _, callback) + callback(nil, vim.fn.json_decode(resp1)) end, - ['textDocument/semanticTokens/full/delta'] = function() - return vim.fn.json_decode(resp2) + ['textDocument/semanticTokens/full/delta'] = function(_, _, callback) + callback(nil, vim.fn.json_decode(resp2)) end, } }) diff --git a/test/functional/plugin/lsp/snippet_spec.lua b/test/functional/plugin/lsp/snippet_spec.lua index ba8bc7fe04..e60c36cd23 100644 --- a/test/functional/plugin/lsp/snippet_spec.lua +++ b/test/functional/plugin/lsp/snippet_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + local snippet = require('vim.lsp._snippet_grammar') local type = snippet.NodeType -local eq = helpers.eq -local exec_lua = helpers.exec_lua +local eq = t.eq +local exec_lua = n.exec_lua describe('vim.lsp._snippet_grammar', function() - before_each(helpers.clear) - after_each(helpers.clear) + before_each(n.clear) + after_each(n.clear) local parse = function(...) local res = exec_lua('return require("vim.lsp._snippet_grammar").parse(...)', ...) diff --git a/test/functional/plugin/lsp/helpers.lua b/test/functional/plugin/lsp/testutil.lua index 97fa108500..3430a1e1a3 100644 --- a/test/functional/plugin/lsp/helpers.lua +++ b/test/functional/plugin/lsp/testutil.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(nil) +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local run = helpers.run -local stop = helpers.stop +local clear = n.clear +local exec_lua = n.exec_lua +local run = n.run +local stop = n.stop +local api = n.api local NIL = vim.NIL local M = {} @@ -38,8 +39,7 @@ M.create_server_definition = [[ }) local handler = handlers[method] if handler then - local response, err = handler(method, params) - callback(err, response) + handler(method, params, callback) elseif method == 'initialize' then callback(nil, { capabilities = opts.capabilities or {} @@ -105,6 +105,11 @@ local function fake_lsp_server_setup(test_name, timeout_ms, options, settings) uri = 'file://' .. vim.uv.cwd(), name = 'test_folder', }}; + before_init = function(params, config) + vim.schedule(function() + vim.rpcrequest(1, "setup") + end) + end, on_init = function(client, result) TEST_RPC_CLIENT = client vim.rpcrequest(1, "init", result) @@ -128,6 +133,18 @@ local function fake_lsp_server_setup(test_name, timeout_ms, options, settings) ) end +--- @class test.lsp.Config +--- @field test_name string +--- @field timeout_ms? integer +--- @field options? table +--- @field settings? table +--- +--- @field on_setup? fun() +--- @field on_init? fun(client: vim.lsp.Client, ...) +--- @field on_handler? fun(...) +--- @field on_exit? fun(code: integer, signal: integer) + +--- @param config test.lsp.Config function M.test_rpc_server(config) if config.test_name then M.clear_notrace() @@ -158,8 +175,15 @@ function M.test_rpc_server(config) end end, }) + --- @type integer, integer local code, signal local function on_request(method, args) + if method == 'setup' then + if config.on_setup then + config.on_setup() + end + return NIL + end if method == 'init' then if config.on_init then config.on_init(client, unpack(args)) @@ -180,14 +204,14 @@ function M.test_rpc_server(config) end end -- TODO specify timeout? - -- run(on_request, on_notify, config.on_setup, 1000) - run(on_request, on_notify, config.on_setup) + -- run(on_request, on_notify, nil, 1000) + run(on_request, on_notify, nil) if config.on_exit then config.on_exit(code, signal) end stop() if config.test_name then - exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })") + api.nvim_exec_autocmds('VimLeavePre', { modeline = false }) end end diff --git a/test/functional/plugin/lsp/utils_spec.lua b/test/functional/plugin/lsp/utils_spec.lua index bb9cdb8390..6c6dec0667 100644 --- a/test/functional/plugin/lsp/utils_spec.lua +++ b/test/functional/plugin/lsp/utils_spec.lua @@ -1,12 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local feed = helpers.feed -local eq = helpers.eq -local exec_lua = helpers.exec_lua +local feed = n.feed +local eq = t.eq +local exec_lua = n.exec_lua describe('vim.lsp.util', function() - before_each(helpers.clear) + before_each(n.clear) describe('stylize_markdown', function() local stylize_markdown = function(content, opts) @@ -142,7 +143,7 @@ describe('vim.lsp.util', function() local screen before_each(function() - helpers.clear() + n.clear() screen = Screen.new(80, 80) screen:attach() feed('79i<CR><Esc>') -- fill screen with empty lines diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 4826153edb..c95a96baca 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -1,36 +1,38 @@ -local helpers = require('test.functional.helpers')(after_each) -local lsp_helpers = require('test.functional.plugin.lsp.helpers') - -local assert_log = helpers.assert_log -local buf_lines = helpers.buf_lines -local clear = helpers.clear -local command = helpers.command -local dedent = helpers.dedent -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local eval = helpers.eval -local matches = helpers.matches -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local t_lsp = require('test.functional.plugin.lsp.testutil') + +local assert_log = t.assert_log +local buf_lines = n.buf_lines +local clear = n.clear +local command = n.command +local dedent = t.dedent +local exec_lua = n.exec_lua +local eq = t.eq +local eval = n.eval +local matches = t.matches +local pcall_err = t.pcall_err local pesc = vim.pesc -local insert = helpers.insert -local fn = helpers.fn -local retry = helpers.retry -local stop = helpers.stop +local insert = n.insert +local fn = n.fn +local retry = t.retry +local stop = n.stop local NIL = vim.NIL -local read_file = require('test.helpers').read_file -local write_file = require('test.helpers').write_file -local is_ci = helpers.is_ci -local api = helpers.api -local is_os = helpers.is_os -local skip = helpers.skip -local mkdir = helpers.mkdir -local tmpname = helpers.tmpname - -local clear_notrace = lsp_helpers.clear_notrace -local create_server_definition = lsp_helpers.create_server_definition -local fake_lsp_code = lsp_helpers.fake_lsp_code -local fake_lsp_logfile = lsp_helpers.fake_lsp_logfile -local test_rpc_server = lsp_helpers.test_rpc_server +local read_file = t.read_file +local write_file = t.write_file +local is_ci = t.is_ci +local api = n.api +local is_os = t.is_os +local skip = t.skip +local mkdir = t.mkdir +local tmpname = t.tmpname + +local clear_notrace = t_lsp.clear_notrace +local create_server_definition = t_lsp.create_server_definition +local fake_lsp_code = t_lsp.fake_lsp_code +local fake_lsp_logfile = t_lsp.fake_lsp_logfile +local test_rpc_server = t_lsp.test_rpc_server local function get_buf_option(name, bufnr) bufnr = bufnr or 'BUFFER' @@ -83,7 +85,7 @@ describe('LSP', function() end) after_each(function() - exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })") + api.nvim_exec_autocmds('VimLeavePre', { modeline = false }) -- exec_lua("lsp.stop_all_clients(true)") end) @@ -146,7 +148,7 @@ describe('LSP', function() after_each(function() stop() exec_lua('lsp.stop_client(lsp.get_clients(), true)') - exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })") + api.nvim_exec_autocmds('VimLeavePre', { modeline = false }) end) it('should run correctly', function() @@ -249,7 +251,7 @@ describe('LSP', function() if is_ci() then pending('hangs the build on CI #14028, re-enable with freeze timeout #14204') return - elseif helpers.skip_fragile(pending) then + elseif t.skip_fragile(pending) then return end local expected_handlers = { @@ -279,7 +281,7 @@ describe('LSP', function() { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'finish', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_finish', on_setup = function() @@ -312,7 +314,7 @@ describe('LSP', function() end) it('should fire autocommands on attach and detach', function() - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_init', on_setup = function() @@ -349,7 +351,7 @@ describe('LSP', function() end) it('should set default options on attach', function() - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'set_defaults_all_capabilities', on_init = function(_client) @@ -397,7 +399,7 @@ describe('LSP', function() end) it('should overwrite options set by ftplugins', function() - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'set_defaults_all_capabilities', on_init = function(_client) @@ -437,7 +439,7 @@ describe('LSP', function() end) it('should not overwrite user-defined options', function() - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'set_defaults_all_capabilities', on_init = function(_client) @@ -473,6 +475,12 @@ describe('LSP', function() local server = _create_server() local bufnr = vim.api.nvim_create_buf(false, true) vim.api.nvim_set_current_buf(bufnr) + local detach_called = false + vim.api.nvim_create_autocmd("LspDetach", { + callback = function() + detach_called = true + end + }) local client_id = vim.lsp.start({ name = 'detach-dummy', cmd = server.cmd }) assert(client_id, "lsp.start must return client_id") local client = vim.lsp.get_client_by_id(client_id) @@ -484,11 +492,42 @@ describe('LSP', function() client_id = client_id, num_attached_before = num_attached_before, num_attached_after = num_attached_after, + detach_called = detach_called, } ]]) eq(true, result ~= nil, 'exec_lua must return result') eq(1, result.num_attached_before) eq(0, result.num_attached_after) + eq(true, result.detach_called) + end) + + it('should not re-attach buffer if it was deleted in on_init #28575', function() + clear() + exec_lua(create_server_definition) + exec_lua([[ + local server = _create_server({ + handlers = { + initialize = function(method, params, callback) + vim.schedule(function() + callback(nil, { capabilities = {} }) + end) + end + } + }) + local bufnr = vim.api.nvim_create_buf(false, true) + local on_init_called = false + local client_id = vim.lsp.start({ + name = 'detach-dummy', + cmd = server.cmd, + on_init = function() + vim.api.nvim_buf_delete(bufnr, {}) + on_init_called = true + end + }) + vim.lsp.buf_attach_client(bufnr, client_id) + local ok = vim.wait(1000, function() return on_init_called end) + assert(ok, "on_init was not called") + ]]) end) it('client should return settings via workspace/configuration handler', function() @@ -508,7 +547,7 @@ describe('LSP', function() }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'check_workspace_configuration', on_init = function(_client) @@ -598,7 +637,7 @@ describe('LSP', function() { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'text_document_sync_save_bool', on_init = function(c) @@ -641,11 +680,11 @@ describe('LSP', function() vim.lsp.stop_client(client_id) return server.messages ]]) - eq(#messages, 4) - eq(messages[1].method, 'initialize') - eq(messages[2].method, 'initialized') - eq(messages[3].method, 'shutdown') - eq(messages[4].method, 'exit') + eq(4, #messages) + eq('initialize', messages[1].method) + eq('initialized', messages[2].method) + eq('shutdown', messages[3].method) + eq('exit', messages[4].method) end) it('BufWritePre sends willSave / willSaveWaitUntil, applies textEdits', function() @@ -660,7 +699,7 @@ describe('LSP', function() } }, handlers = { - ['textDocument/willSaveWaitUntil'] = function() + ['textDocument/willSaveWaitUntil'] = function(_, _, callback) local text_edit = { range = { start = { line = 0, character = 0 }, @@ -668,7 +707,7 @@ describe('LSP', function() }, newText = 'Hello' } - return { text_edit, } + callback(nil, { text_edit, }) end }, }) @@ -692,7 +731,7 @@ describe('LSP', function() { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server({ test_name = 'text_document_save_did_open', on_init = function(c) @@ -732,7 +771,7 @@ describe('LSP', function() { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'text_document_sync_save_includeText', on_init = function(c) @@ -808,14 +847,12 @@ describe('LSP', function() BUFFER = vim.api.nvim_get_current_buf() lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID) vim.lsp.handlers['textDocument/typeDefinition'] = function() end + vim.cmd(BUFFER.."bwipeout") ]=]) end, on_init = function(client) client.stop() exec_lua('vim.lsp.buf.type_definition()') - exec_lua [[ - vim.api.nvim_command(BUFFER.."bwipeout") - ]] end, on_exit = function(code, signal) eq(0, code, 'exit code') @@ -859,7 +896,7 @@ describe('LSP', function() local expected_handlers = { { NIL, {}, { method = 'finish', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'check_forward_request_cancelled', on_init = function(_client) @@ -885,7 +922,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { { code = -32801 }, NIL, { method = 'error_code_test', bufnr = 1, client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'check_forward_content_modified', on_init = function(_client) @@ -915,7 +952,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'check_pending_request_tracked', on_init = function(_client) @@ -949,7 +986,7 @@ describe('LSP', function() local expected_handlers = { { NIL, {}, { method = 'finish', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'check_cancel_request_tracked', on_init = function(_client) @@ -982,7 +1019,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'check_tracked_requests_cleared', on_init = function(_client) @@ -1021,7 +1058,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'check_tracked_requests_cleared', on_init = function(_client) @@ -1057,7 +1094,7 @@ describe('LSP', function() { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'finish', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_finish', on_setup = function() @@ -1067,12 +1104,10 @@ describe('LSP', function() "testing"; "123"; }) - ]] - eq(1, exec_lua('return TEST_RPC_CLIENT_ID')) - eq(true, exec_lua('return lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)')) - eq(true, exec_lua('return lsp.buf_is_attached(BUFFER, TEST_RPC_CLIENT_ID)')) - exec_lua [[ - vim.api.nvim_command(BUFFER.."bwipeout") + assert(TEST_RPC_CLIENT_ID == 1) + assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)) + assert(lsp.buf_is_attached(BUFFER, TEST_RPC_CLIENT_ID)) + vim.cmd(BUFFER.."bwipeout") ]] end, on_init = function(_client) @@ -1101,7 +1136,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open', on_setup = function() @@ -1147,7 +1182,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open', on_setup = function() @@ -1190,7 +1225,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open_and_change', on_setup = function() @@ -1238,7 +1273,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open_and_change_noeol', on_setup = function() @@ -1305,7 +1340,7 @@ describe('LSP', function() }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'inlay_hint', on_setup = function() @@ -1332,7 +1367,7 @@ describe('LSP', function() on_handler = function(err, result, ctx) if ctx.method == 'start' then exec_lua [[ - vim.lsp.inlay_hint.enable(BUFFER) + vim.lsp.inlay_hint.enable(true, { bufnr = BUFFER }) ]] end if ctx.method == 'textDocument/inlayHint' then @@ -1352,7 +1387,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open_and_change_incremental', options = { @@ -1403,7 +1438,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open_and_change_incremental', options = { @@ -1457,7 +1492,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open_and_change_incremental_editing', on_setup = function() @@ -1485,7 +1520,7 @@ describe('LSP', function() end, on_handler = function(err, result, ctx) if ctx.method == 'start' then - helpers.command('normal! 1Go') + n.command('normal! 1Go') client.notify('finish') end eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') @@ -1502,7 +1537,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open_and_change_multi', on_setup = function() @@ -1553,7 +1588,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open_and_change_multi_and_close', on_setup = function() @@ -1607,7 +1642,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'invalid_header', on_setup = function() end, @@ -1640,7 +1675,7 @@ describe('LSP', function() }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'decode_nil', on_setup = function() @@ -1807,7 +1842,7 @@ describe('LSP', function() make_edit(1, 0, 2, 5, 'foobar'), make_edit(4, 0, 5, 0, 'barfoo'), } - eq(true, exec_lua('return vim.api.nvim_buf_set_mark(1, "a", 2, 1, {})')) + eq(true, api.nvim_buf_set_mark(1, 'a', 2, 1, {})) exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, 'utf-16') eq({ 'First line of text', @@ -1815,7 +1850,7 @@ describe('LSP', function() 'Fourth line of text', 'barfoo', }, buf_lines(1)) - local mark = exec_lua('return vim.api.nvim_buf_get_mark(1, "a")') + local mark = api.nvim_buf_get_mark(1, 'a') eq({ 2, 1 }, mark) end) @@ -1824,7 +1859,7 @@ describe('LSP', function() make_edit(1, 0, 2, 15, 'foobar'), make_edit(4, 0, 5, 0, 'barfoo'), } - eq(true, exec_lua('return vim.api.nvim_buf_set_mark(1, "a", 2, 10, {})')) + eq(true, api.nvim_buf_set_mark(1, 'a', 2, 10, {})) exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, 'utf-16') eq({ 'First line of text', @@ -1832,7 +1867,7 @@ describe('LSP', function() 'Fourth line of text', 'barfoo', }, buf_lines(1)) - local mark = exec_lua('return vim.api.nvim_buf_get_mark(1, "a")') + local mark = api.nvim_buf_get_mark(1, 'a') eq({ 2, 9 }, mark) end) @@ -1841,19 +1876,19 @@ describe('LSP', function() make_edit(1, 0, 4, 5, 'foobar'), make_edit(4, 0, 5, 0, 'barfoo'), } - eq(true, exec_lua('return vim.api.nvim_buf_set_mark(1, "a", 4, 1, {})')) + eq(true, api.nvim_buf_set_mark(1, 'a', 4, 1, {})) exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, 'utf-16') eq({ 'First line of text', 'foobaro', }, buf_lines(1)) - local mark = exec_lua('return vim.api.nvim_buf_get_mark(1, "a")') + local mark = api.nvim_buf_get_mark(1, 'a') eq({ 2, 1 }, mark) end) describe('cursor position', function() it("don't fix the cursor if the range contains the cursor", function() - fn.nvim_win_set_cursor(0, { 2, 6 }) + api.nvim_win_set_cursor(0, { 2, 6 }) local edits = { make_edit(1, 0, 1, 19, 'Second line of text'), } @@ -1865,11 +1900,11 @@ describe('LSP', function() 'Fourth line of text', 'å å ɧ 汉语 ↥ 🤦 🦄', }, buf_lines(1)) - eq({ 2, 6 }, fn.nvim_win_get_cursor(0)) + eq({ 2, 6 }, api.nvim_win_get_cursor(0)) end) it('fix the cursor to the valid col if the content was removed', function() - fn.nvim_win_set_cursor(0, { 2, 6 }) + api.nvim_win_set_cursor(0, { 2, 6 }) local edits = { make_edit(1, 0, 1, 6, ''), make_edit(1, 6, 1, 19, ''), @@ -1882,11 +1917,11 @@ describe('LSP', function() 'Fourth line of text', 'å å ɧ 汉语 ↥ 🤦 🦄', }, buf_lines(1)) - eq({ 2, 0 }, fn.nvim_win_get_cursor(0)) + eq({ 2, 0 }, api.nvim_win_get_cursor(0)) end) it('fix the cursor to the valid row if the content was removed', function() - fn.nvim_win_set_cursor(0, { 2, 6 }) + api.nvim_win_set_cursor(0, { 2, 6 }) local edits = { make_edit(1, 0, 1, 6, ''), make_edit(0, 18, 5, 0, ''), @@ -1895,11 +1930,11 @@ describe('LSP', function() eq({ 'First line of text', }, buf_lines(1)) - eq({ 1, 17 }, fn.nvim_win_get_cursor(0)) + eq({ 1, 17 }, api.nvim_win_get_cursor(0)) end) it('fix the cursor row', function() - fn.nvim_win_set_cursor(0, { 3, 0 }) + api.nvim_win_set_cursor(0, { 3, 0 }) local edits = { make_edit(1, 0, 2, 0, ''), } @@ -1910,14 +1945,14 @@ describe('LSP', function() 'Fourth line of text', 'å å ɧ 汉语 ↥ 🤦 🦄', }, buf_lines(1)) - eq({ 2, 0 }, fn.nvim_win_get_cursor(0)) + eq({ 2, 0 }, api.nvim_win_get_cursor(0)) end) it('fix the cursor col', function() -- append empty last line. See #22636 - exec_lua('vim.api.nvim_buf_set_lines(...)', 1, -1, -1, true, { '' }) + api.nvim_buf_set_lines(1, -1, -1, true, { '' }) - fn.nvim_win_set_cursor(0, { 2, 11 }) + api.nvim_win_set_cursor(0, { 2, 11 }) local edits = { make_edit(1, 7, 1, 11, ''), } @@ -1930,11 +1965,11 @@ describe('LSP', function() 'å å ɧ 汉语 ↥ 🤦 🦄', '', }, buf_lines(1)) - eq({ 2, 7 }, fn.nvim_win_get_cursor(0)) + eq({ 2, 7 }, api.nvim_win_get_cursor(0)) end) it('fix the cursor row and col', function() - fn.nvim_win_set_cursor(0, { 2, 12 }) + api.nvim_win_set_cursor(0, { 2, 12 }) local edits = { make_edit(0, 11, 1, 12, ''), } @@ -1945,7 +1980,7 @@ describe('LSP', function() 'Fourth line of text', 'å å ɧ 汉语 ↥ 🤦 🦄', }, buf_lines(1)) - eq({ 1, 11 }, fn.nvim_win_get_cursor(0)) + eq({ 1, 11 }, api.nvim_win_get_cursor(0)) end) end) @@ -2021,7 +2056,7 @@ describe('LSP', function() end) describe('apply_text_document_edit', function() - local target_bufnr + local target_bufnr --- @type integer local text_document_edit = function(editVersion) return { edits = { @@ -2267,7 +2302,7 @@ describe('LSP', function() }, } exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16') - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', tmpfile)) + eq(true, vim.uv.fs_stat(tmpfile) ~= nil) end) it( 'Supports file creation in folder that needs to be created with CreateFile payload', @@ -2285,7 +2320,7 @@ describe('LSP', function() }, } exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16') - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', tmpfile)) + eq(true, vim.uv.fs_stat(tmpfile) ~= nil) end ) it('createFile does not touch file if it exists and ignoreIfExists is set', function() @@ -2304,7 +2339,7 @@ describe('LSP', function() }, } exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16') - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', tmpfile)) + eq(true, vim.uv.fs_stat(tmpfile) ~= nil) eq('Dummy content', read_file(tmpfile)) end) it('createFile overrides file if overwrite is set', function() @@ -2324,7 +2359,7 @@ describe('LSP', function() }, } exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16') - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', tmpfile)) + eq(true, vim.uv.fs_stat(tmpfile) ~= nil) eq('', read_file(tmpfile)) end) it('DeleteFile delete file and buffer', function() @@ -2348,8 +2383,8 @@ describe('LSP', function() }, } eq(true, pcall(exec_lua, 'vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16')) - eq(false, exec_lua('return vim.uv.fs_stat(...) ~= nil', tmpfile)) - eq(false, exec_lua('return vim.api.nvim_buf_is_loaded(vim.fn.bufadd(...))', tmpfile)) + eq(false, vim.uv.fs_stat(tmpfile) ~= nil) + eq(false, api.nvim_buf_is_loaded(fn.bufadd(tmpfile))) end) it('DeleteFile fails if file does not exist and ignoreIfNotExists is false', function() local tmpfile = tmpname() @@ -2367,12 +2402,12 @@ describe('LSP', function() }, } eq(false, pcall(exec_lua, 'vim.lsp.util.apply_workspace_edit(...)', edit)) - eq(false, exec_lua('return vim.uv.fs_stat(...) ~= nil', tmpfile)) + eq(false, vim.uv.fs_stat(tmpfile) ~= nil) end) end) describe('lsp.util.rename', function() - local pathsep = helpers.get_pathsep() + local pathsep = n.get_pathsep() it('Can rename an existing file', function() local old = tmpname() @@ -2395,9 +2430,9 @@ describe('LSP', function() new ) eq({ 'Test content' }, lines) - local exists = exec_lua('return vim.uv.fs_stat(...) ~= nil', old) + local exists = vim.uv.fs_stat(old) ~= nil eq(false, exists) - exists = exec_lua('return vim.uv.fs_stat(...) ~= nil', new) + exists = vim.uv.fs_stat(new) ~= nil eq(true, exists) os.remove(new) end) @@ -2408,7 +2443,7 @@ describe('LSP', function() os.remove(old_dir) os.remove(new_dir) - helpers.mkdir_p(old_dir) + n.mkdir_p(old_dir) local file = 'file.txt' write_file(old_dir .. pathsep .. file, 'Test content') @@ -2433,9 +2468,9 @@ describe('LSP', function() file ) eq({ 'Test content' }, lines) - eq(false, exec_lua('return vim.uv.fs_stat(...) ~= nil', old_dir)) - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', new_dir)) - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', new_dir .. pathsep .. file)) + eq(false, vim.uv.fs_stat(old_dir) ~= nil) + eq(true, vim.uv.fs_stat(new_dir) ~= nil) + eq(true, vim.uv.fs_stat(new_dir .. pathsep .. file) ~= nil) os.remove(new_dir) end) @@ -2444,7 +2479,7 @@ describe('LSP', function() local new = tmpname() os.remove(old) os.remove(new) - helpers.mkdir_p(old) + n.mkdir_p(old) local result = exec_lua( [[ @@ -2499,7 +2534,7 @@ describe('LSP', function() new ) - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', old)) + eq(true, vim.uv.fs_stat(old) ~= nil) eq('New file', read_file(new)) exec_lua( @@ -2513,7 +2548,7 @@ describe('LSP', function() new ) - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', old)) + eq(true, vim.uv.fs_stat(old) ~= nil) eq('New file', read_file(new)) end ) @@ -2543,8 +2578,8 @@ describe('LSP', function() old, new ) - eq(false, exec_lua('return vim.uv.fs_stat(...) ~= nil', old)) - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', new)) + eq(false, vim.uv.fs_stat(old) ~= nil) + eq(true, vim.uv.fs_stat(new) ~= nil) eq(true, undo_kept) end) it('Maintains undo information for unloaded buffer', function() @@ -2570,8 +2605,8 @@ describe('LSP', function() old, new ) - eq(false, exec_lua('return vim.uv.fs_stat(...) ~= nil', old)) - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', new)) + eq(false, vim.uv.fs_stat(old) ~= nil) + eq(true, vim.uv.fs_stat(new) ~= nil) eq(true, undo_kept) end) it('Does not rename file when it conflicts with a buffer without file', function() @@ -2615,8 +2650,8 @@ describe('LSP', function() new ) - eq(false, exec_lua('return vim.uv.fs_stat(...) ~= nil', old)) - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', new)) + eq(false, vim.uv.fs_stat(old) ~= nil) + eq(true, vim.uv.fs_stat(new) ~= nil) eq('Old file', read_file(new)) end) end) @@ -2697,6 +2732,7 @@ describe('LSP', function() eq(expected, actual) end) end) + describe('lsp.util.symbols_to_items', function() describe('convert DocumentSymbol[] to items', function() it('DocumentSymbol has children', function() @@ -2971,7 +3007,7 @@ describe('LSP', function() end) describe('lsp.util.jump_to_location', function() - local target_bufnr + local target_bufnr --- @type integer before_each(function() target_bufnr = exec_lua [[ @@ -2994,10 +3030,10 @@ describe('LSP', function() local jump = function(msg) eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg, 'utf-16')) - eq(target_bufnr, exec_lua [[return vim.fn.bufnr('%')]]) + eq(target_bufnr, fn.bufnr('%')) return { - line = exec_lua [[return vim.fn.line('.')]], - col = exec_lua [[return vim.fn.col('.')]], + line = fn.line('.'), + col = fn.col('.'), } end @@ -3027,25 +3063,25 @@ describe('LSP', function() local pos = jump(location(1, 2, 1, 2)) eq(2, pos.line) eq(4, pos.col) - eq('å', exec_lua [[return vim.fn.expand('<cword>')]]) + eq('å', fn.expand('<cword>')) end) it('adds current position to jumplist before jumping', function() - fn.nvim_win_set_buf(0, target_bufnr) - local mark = fn.nvim_buf_get_mark(target_bufnr, "'") + api.nvim_win_set_buf(0, target_bufnr) + local mark = api.nvim_buf_get_mark(target_bufnr, "'") eq({ 1, 0 }, mark) - fn.nvim_win_set_cursor(0, { 2, 3 }) + api.nvim_win_set_cursor(0, { 2, 3 }) jump(location(0, 9, 0, 9)) - mark = fn.nvim_buf_get_mark(target_bufnr, "'") + mark = api.nvim_buf_get_mark(target_bufnr, "'") eq({ 2, 3 }, mark) end) end) describe('lsp.util.show_document', function() - local target_bufnr - local target_bufnr2 + local target_bufnr --- @type integer + local target_bufnr2 --- @type integer before_each(function() target_bufnr = exec_lua([[ @@ -3084,11 +3120,11 @@ describe('LSP', function() ) ) if focus == true or focus == nil then - eq(target_bufnr, exec_lua([[return vim.fn.bufnr('%')]])) + eq(target_bufnr, fn.bufnr('%')) end return { - line = exec_lua([[return vim.fn.line('.')]]), - col = exec_lua([[return vim.fn.col('.')]]), + line = fn.line('.'), + col = fn.col('.'), } end @@ -3132,101 +3168,101 @@ describe('LSP', function() end) it('does not add current position to jumplist if not focus', function() - fn.nvim_win_set_buf(0, target_bufnr) - local mark = fn.nvim_buf_get_mark(target_bufnr, "'") + api.nvim_win_set_buf(0, target_bufnr) + local mark = api.nvim_buf_get_mark(target_bufnr, "'") eq({ 1, 0 }, mark) - fn.nvim_win_set_cursor(0, { 2, 3 }) + api.nvim_win_set_cursor(0, { 2, 3 }) show_document(location(0, 9, 0, 9), false, true) show_document(location(0, 9, 0, 9, true), false, true) - mark = fn.nvim_buf_get_mark(target_bufnr, "'") + mark = api.nvim_buf_get_mark(target_bufnr, "'") eq({ 1, 0 }, mark) end) it('does not change cursor position if not focus and not reuse_win', function() - fn.nvim_win_set_buf(0, target_bufnr) - local cursor = fn.nvim_win_get_cursor(0) + api.nvim_win_set_buf(0, target_bufnr) + local cursor = api.nvim_win_get_cursor(0) show_document(location(0, 9, 0, 9), false, false) - eq(cursor, fn.nvim_win_get_cursor(0)) + eq(cursor, api.nvim_win_get_cursor(0)) end) it('does not change window if not focus', function() - fn.nvim_win_set_buf(0, target_bufnr) - local win = fn.nvim_get_current_win() + api.nvim_win_set_buf(0, target_bufnr) + local win = api.nvim_get_current_win() -- same document/bufnr show_document(location(0, 9, 0, 9), false, true) - eq(win, fn.nvim_get_current_win()) + eq(win, api.nvim_get_current_win()) -- different document/bufnr, new window/split show_document(location(0, 9, 0, 9, true), false, true) - eq(2, #fn.nvim_list_wins()) - eq(win, fn.nvim_get_current_win()) + eq(2, #api.nvim_list_wins()) + eq(win, api.nvim_get_current_win()) end) it("respects 'reuse_win' parameter", function() - fn.nvim_win_set_buf(0, target_bufnr) + api.nvim_win_set_buf(0, target_bufnr) -- does not create a new window if the buffer is already open show_document(location(0, 9, 0, 9), false, true) - eq(1, #fn.nvim_list_wins()) + eq(1, #api.nvim_list_wins()) -- creates a new window even if the buffer is already open show_document(location(0, 9, 0, 9), false, false) - eq(2, #fn.nvim_list_wins()) + eq(2, #api.nvim_list_wins()) end) it('correctly sets the cursor of the split if range is given without focus', function() - fn.nvim_win_set_buf(0, target_bufnr) + api.nvim_win_set_buf(0, target_bufnr) show_document(location(0, 9, 0, 9, true), false, true) - local wins = fn.nvim_list_wins() + local wins = api.nvim_list_wins() eq(2, #wins) table.sort(wins) - eq({ 1, 0 }, fn.nvim_win_get_cursor(wins[1])) - eq({ 1, 9 }, fn.nvim_win_get_cursor(wins[2])) + eq({ 1, 0 }, api.nvim_win_get_cursor(wins[1])) + eq({ 1, 9 }, api.nvim_win_get_cursor(wins[2])) end) it('does not change cursor of the split if not range and not focus', function() - fn.nvim_win_set_buf(0, target_bufnr) - fn.nvim_win_set_cursor(0, { 2, 3 }) + api.nvim_win_set_buf(0, target_bufnr) + api.nvim_win_set_cursor(0, { 2, 3 }) exec_lua([[vim.cmd.new()]]) - fn.nvim_win_set_buf(0, target_bufnr2) - fn.nvim_win_set_cursor(0, { 2, 3 }) + api.nvim_win_set_buf(0, target_bufnr2) + api.nvim_win_set_cursor(0, { 2, 3 }) show_document({ uri = 'file:///fake/uri2' }, false, true) - local wins = fn.nvim_list_wins() + local wins = api.nvim_list_wins() eq(2, #wins) - eq({ 2, 3 }, fn.nvim_win_get_cursor(wins[1])) - eq({ 2, 3 }, fn.nvim_win_get_cursor(wins[2])) + eq({ 2, 3 }, api.nvim_win_get_cursor(wins[1])) + eq({ 2, 3 }, api.nvim_win_get_cursor(wins[2])) end) it('respects existing buffers', function() - fn.nvim_win_set_buf(0, target_bufnr) - local win = fn.nvim_get_current_win() + api.nvim_win_set_buf(0, target_bufnr) + local win = api.nvim_get_current_win() exec_lua([[vim.cmd.new()]]) - fn.nvim_win_set_buf(0, target_bufnr2) - fn.nvim_win_set_cursor(0, { 2, 3 }) - local split = fn.nvim_get_current_win() + api.nvim_win_set_buf(0, target_bufnr2) + api.nvim_win_set_cursor(0, { 2, 3 }) + local split = api.nvim_get_current_win() -- reuse win for open document/bufnr if called from split show_document(location(0, 9, 0, 9, true), false, true) - eq({ 1, 9 }, fn.nvim_win_get_cursor(split)) - eq(2, #fn.nvim_list_wins()) + eq({ 1, 9 }, api.nvim_win_get_cursor(split)) + eq(2, #api.nvim_list_wins()) - fn.nvim_set_current_win(win) + api.nvim_set_current_win(win) -- reuse win for open document/bufnr if called outside the split show_document(location(0, 9, 0, 9, true), false, true) - eq({ 1, 9 }, fn.nvim_win_get_cursor(split)) - eq(2, #fn.nvim_list_wins()) + eq({ 1, 9 }, api.nvim_win_get_cursor(split)) + eq(2, #api.nvim_list_wins()) end) end) @@ -3464,6 +3500,442 @@ describe('LSP', function() end) end) + describe('vim.lsp.buf.typehierarchy subtypes', function() + it('does nothing for an empty response', function() + local qflist_count = exec_lua([=[ + require'vim.lsp.handlers'['typeHierarchy/subtypes'](nil, nil, {}) + return #vim.fn.getqflist() + ]=]) + eq(0, qflist_count) + end) + + it('opens the quickfix list with the right subtypes', function() + clear() + exec_lua(create_server_definition) + local qflist = exec_lua([=[ + local clangd_response = { { + data = { + parents = { { + parents = { { + parents = { { + parents = {}, + symbolID = "62B3D268A01B9978" + } }, + symbolID = "DC9B0AD433B43BEC" + } }, + symbolID = "06B5F6A19BA9F6A8" + } }, + symbolID = "EDC336589C09ABB2" + }, + kind = 5, + name = "D2", + range = { + ["end"] = { + character = 8, + line = 9 + }, + start = { + character = 6, + line = 9 + } + }, + selectionRange = { + ["end"] = { + character = 8, + line = 9 + }, + start = { + character = 6, + line = 9 + } + }, + uri = "file:///home/jiangyinzuo/hello.cpp" + }, { + data = { + parents = { { + parents = { { + parents = { { + parents = {}, + symbolID = "62B3D268A01B9978" + } }, + symbolID = "DC9B0AD433B43BEC" + } }, + symbolID = "06B5F6A19BA9F6A8" + } }, + symbolID = "AFFCAED15557EF08" + }, + kind = 5, + name = "D1", + range = { + ["end"] = { + character = 8, + line = 8 + }, + start = { + character = 6, + line = 8 + } + }, + selectionRange = { + ["end"] = { + character = 8, + line = 8 + }, + start = { + character = 6, + line = 8 + } + }, + uri = "file:///home/jiangyinzuo/hello.cpp" + } } + + local server = _create_server({ + capabilities = { + positionEncoding = "utf-8" + }, + }) + local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + local handler = require'vim.lsp.handlers'['typeHierarchy/subtypes'] + handler(nil, clangd_response, { client_id = client_id, bufnr = 1 }) + return vim.fn.getqflist() + ]=]) + + local expected = { + { + bufnr = 2, + col = 7, + end_col = 0, + end_lnum = 0, + lnum = 10, + module = '', + nr = 0, + pattern = '', + text = 'D2', + type = '', + valid = 1, + vcol = 0, + }, + { + bufnr = 2, + col = 7, + end_col = 0, + end_lnum = 0, + lnum = 9, + module = '', + nr = 0, + pattern = '', + text = 'D1', + type = '', + valid = 1, + vcol = 0, + }, + } + + eq(expected, qflist) + end) + + it('opens the quickfix list with the right subtypes and details', function() + clear() + exec_lua(create_server_definition) + local qflist = exec_lua([=[ + local jdtls_response = { + { + data = { element = '=hello-java_ed323c3c/_<{Main.java[Main[A' }, + detail = '', + kind = 5, + name = 'A', + range = { + ['end'] = { character = 26, line = 3 }, + start = { character = 1, line = 3 }, + }, + selectionRange = { + ['end'] = { character = 8, line = 3 }, + start = { character = 7, line = 3 }, + }, + tags = {}, + uri = 'file:///home/jiangyinzuo/hello-java/Main.java', + }, + { + data = { element = '=hello-java_ed323c3c/_<mylist{MyList.java[MyList[Inner' }, + detail = 'mylist', + kind = 5, + name = 'MyList$Inner', + range = { + ['end'] = { character = 37, line = 3 }, + start = { character = 1, line = 3 }, + }, + selectionRange = { + ['end'] = { character = 19, line = 3 }, + start = { character = 14, line = 3 }, + }, + tags = {}, + uri = 'file:///home/jiangyinzuo/hello-java/mylist/MyList.java', + }, + } + + local server = _create_server({ + capabilities = { + positionEncoding = "utf-8" + }, + }) + local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + local handler = require'vim.lsp.handlers'['typeHierarchy/subtypes'] + handler(nil, jdtls_response, { client_id = client_id, bufnr = 1 }) + return vim.fn.getqflist() + ]=]) + + local expected = { + { + bufnr = 2, + col = 2, + end_col = 0, + end_lnum = 0, + lnum = 4, + module = '', + nr = 0, + pattern = '', + text = 'A', + type = '', + valid = 1, + vcol = 0, + }, + { + bufnr = 3, + col = 2, + end_col = 0, + end_lnum = 0, + lnum = 4, + module = '', + nr = 0, + pattern = '', + text = 'MyList$Inner mylist', + type = '', + valid = 1, + vcol = 0, + }, + } + eq(expected, qflist) + end) + end) + + describe('vim.lsp.buf.typehierarchy supertypes', function() + it('does nothing for an empty response', function() + local qflist_count = exec_lua([=[ + require'vim.lsp.handlers'['typeHierarchy/supertypes'](nil, nil, {}) + return #vim.fn.getqflist() + ]=]) + eq(0, qflist_count) + end) + + it('opens the quickfix list with the right supertypes', function() + clear() + exec_lua(create_server_definition) + local qflist = exec_lua([=[ + local clangd_response = { { + data = { + parents = { { + parents = { { + parents = { { + parents = {}, + symbolID = "62B3D268A01B9978" + } }, + symbolID = "DC9B0AD433B43BEC" + } }, + symbolID = "06B5F6A19BA9F6A8" + } }, + symbolID = "EDC336589C09ABB2" + }, + kind = 5, + name = "D2", + range = { + ["end"] = { + character = 8, + line = 9 + }, + start = { + character = 6, + line = 9 + } + }, + selectionRange = { + ["end"] = { + character = 8, + line = 9 + }, + start = { + character = 6, + line = 9 + } + }, + uri = "file:///home/jiangyinzuo/hello.cpp" + }, { + data = { + parents = { { + parents = { { + parents = { { + parents = {}, + symbolID = "62B3D268A01B9978" + } }, + symbolID = "DC9B0AD433B43BEC" + } }, + symbolID = "06B5F6A19BA9F6A8" + } }, + symbolID = "AFFCAED15557EF08" + }, + kind = 5, + name = "D1", + range = { + ["end"] = { + character = 8, + line = 8 + }, + start = { + character = 6, + line = 8 + } + }, + selectionRange = { + ["end"] = { + character = 8, + line = 8 + }, + start = { + character = 6, + line = 8 + } + }, + uri = "file:///home/jiangyinzuo/hello.cpp" + } } + + local server = _create_server({ + capabilities = { + positionEncoding = "utf-8" + }, + }) + local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + local handler = require'vim.lsp.handlers'['typeHierarchy/supertypes'] + handler(nil, clangd_response, { client_id = client_id, bufnr = 1 }) + return vim.fn.getqflist() + ]=]) + + local expected = { + { + bufnr = 2, + col = 7, + end_col = 0, + end_lnum = 0, + lnum = 10, + module = '', + nr = 0, + pattern = '', + text = 'D2', + type = '', + valid = 1, + vcol = 0, + }, + { + bufnr = 2, + col = 7, + end_col = 0, + end_lnum = 0, + lnum = 9, + module = '', + nr = 0, + pattern = '', + text = 'D1', + type = '', + valid = 1, + vcol = 0, + }, + } + + eq(expected, qflist) + end) + + it('opens the quickfix list with the right supertypes and details', function() + clear() + exec_lua(create_server_definition) + local qflist = exec_lua([=[ + local jdtls_response = { + { + data = { element = '=hello-java_ed323c3c/_<{Main.java[Main[A' }, + detail = '', + kind = 5, + name = 'A', + range = { + ['end'] = { character = 26, line = 3 }, + start = { character = 1, line = 3 }, + }, + selectionRange = { + ['end'] = { character = 8, line = 3 }, + start = { character = 7, line = 3 }, + }, + tags = {}, + uri = 'file:///home/jiangyinzuo/hello-java/Main.java', + }, + { + data = { element = '=hello-java_ed323c3c/_<mylist{MyList.java[MyList[Inner' }, + detail = 'mylist', + kind = 5, + name = 'MyList$Inner', + range = { + ['end'] = { character = 37, line = 3 }, + start = { character = 1, line = 3 }, + }, + selectionRange = { + ['end'] = { character = 19, line = 3 }, + start = { character = 14, line = 3 }, + }, + tags = {}, + uri = 'file:///home/jiangyinzuo/hello-java/mylist/MyList.java', + }, + } + + local server = _create_server({ + capabilities = { + positionEncoding = "utf-8" + }, + }) + local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + local handler = require'vim.lsp.handlers'['typeHierarchy/supertypes'] + handler(nil, jdtls_response, { client_id = client_id, bufnr = 1 }) + return vim.fn.getqflist() + ]=]) + + local expected = { + { + bufnr = 2, + col = 2, + end_col = 0, + end_lnum = 0, + lnum = 4, + module = '', + nr = 0, + pattern = '', + text = 'A', + type = '', + valid = 1, + vcol = 0, + }, + { + bufnr = 3, + col = 2, + end_col = 0, + end_lnum = 0, + lnum = 4, + module = '', + nr = 0, + pattern = '', + text = 'MyList$Inner mylist', + type = '', + valid = 1, + vcol = 0, + }, + } + eq(expected, qflist) + end) + end) + describe('vim.lsp.buf.rename', function() for _, test in ipairs({ { @@ -3504,7 +3976,7 @@ describe('LSP', function() }, }) do it(test.it, function() - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = test.name, on_init = function(_client) @@ -3554,7 +4026,7 @@ describe('LSP', function() describe('vim.lsp.buf.code_action', function() it('Calls client side command if available', function() - local client + local client --- @type vim.lsp.Client local expected_handlers = { { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, @@ -3592,7 +4064,7 @@ describe('LSP', function() } end) it('Calls workspace/executeCommand if no client side command', function() - local client + local client --- @type vim.lsp.Client local expected_handlers = { { NIL, {}, { method = 'shutdown', client_id = 1 } }, { @@ -3632,7 +4104,7 @@ describe('LSP', function() }) end) it('Filters and automatically applies action if requested', function() - local client + local client --- @type vim.lsp.Client local expected_handlers = { { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, @@ -3701,8 +4173,8 @@ describe('LSP', function() } }, handlers = { - ["textDocument/codeAction"] = function() - return { + ["textDocument/codeAction"] = function(_, _, callback) + callback(nil, { { title = "Code Action 1", command = { @@ -3710,10 +4182,10 @@ describe('LSP', function() command = "command:1", } } - } + }) end, - ["codeAction/resolve"] = function() - return nil, "resolve failed" + ["codeAction/resolve"] = function(_, _, callback) + callback("resolve failed", nil) end, } }) @@ -3732,6 +4204,7 @@ describe('LSP', function() eq('command:1', result[5].params.command) end) end) + describe('vim.lsp.commands', function() it('Accepts only string keys', function() matches( @@ -3746,9 +4219,10 @@ describe('LSP', function() ) end) end) + describe('vim.lsp.codelens', function() it('uses client commands', function() - local client + local client --- @type vim.lsp.Client local expected_handlers = { { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, @@ -3802,7 +4276,7 @@ describe('LSP', function() end) it('releases buffer refresh lock', function() - local client + local client --- @type vim.lsp.Client local expected_handlers = { { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, @@ -3879,11 +4353,97 @@ describe('LSP', function() end, } end) + + it('refresh multiple buffers', function() + local lens_title_per_fake_uri = { + ['file:///fake/uri1'] = 'Lens1', + ['file:///fake/uri2'] = 'Lens2', + } + clear() + exec_lua(create_server_definition) + + -- setup lsp + exec_lua( + [[ + local lens_title_per_fake_uri = ... + local server = _create_server({ + capabilities = { + codeLensProvider = { + resolveProvider = true + }, + }, + handlers = { + ["textDocument/codeLens"] = function(method, params, callback) + local lenses = { + { + range = { + start = { line = 0, character = 0 }, + ['end'] = { line = 0, character = 0 }, + }, + command = { + title = lens_title_per_fake_uri[params.textDocument.uri], + command = 'Dummy', + }, + }, + } + callback(nil, lenses) + end, + } + }) + + CLIENT_ID = vim.lsp.start({ + name = "dummy", + cmd = server.cmd, + }) + ]], + lens_title_per_fake_uri + ) + + -- create buffers and setup handler + exec_lua( + [[ + local lens_title_per_fake_uri = ... + local default_buf = vim.api.nvim_get_current_buf() + for fake_uri, _ in pairs(lens_title_per_fake_uri) do + local bufnr = vim.uri_to_bufnr(fake_uri) + vim.api.nvim_set_current_buf(bufnr) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {'Some contents'}) + vim.lsp.buf_attach_client(bufnr, CLIENT_ID) + end + vim.api.nvim_buf_delete(default_buf, {force = true}) + + REQUEST_COUNT = vim.tbl_count(lens_title_per_fake_uri) + RESPONSES = {} + local on_codelens = vim.lsp.codelens.on_codelens + vim.lsp.codelens.on_codelens = function (err, result, ctx, ...) + table.insert(RESPONSES, { err = err, result = result, ctx = ctx }) + return on_codelens(err, result, ctx, ...) + end + ]], + lens_title_per_fake_uri + ) + + -- call codelens refresh + local cmds = exec_lua([[ + RESPONSES = {} + vim.lsp.codelens.refresh() + vim.wait(100, function () return #RESPONSES >= REQUEST_COUNT end) + + local cmds = {} + for _, resp in ipairs(RESPONSES) do + local uri = resp.ctx.params.textDocument.uri + cmds[uri] = resp.result[1].command + end + return cmds + ]]) + eq({ command = 'Dummy', title = 'Lens1' }, cmds['file:///fake/uri1']) + eq({ command = 'Dummy', title = 'Lens2' }, cmds['file:///fake/uri2']) + end) end) describe('vim.lsp.buf.format', function() it('Aborts with notify if no client matches filter', function() - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_init', on_init = function(c) @@ -3912,7 +4472,7 @@ describe('LSP', function() { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_formatting', on_init = function(c) @@ -3945,7 +4505,7 @@ describe('LSP', function() { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_formatting', on_init = function(c) @@ -4068,6 +4628,9 @@ describe('LSP', function() end ]]) local fail_msg = '[LSP] Format request failed, no matching language servers.' + --- @param name string + --- @param formatting boolean + --- @param range_formatting boolean local function check_notify(name, formatting, range_formatting) local timeout_msg = '[LSP][' .. name .. '] timeout' exec_lua( @@ -4131,13 +4694,13 @@ describe('LSP', function() }, handlers = { ---@return lsp.Location[] - ['textDocument/definition'] = function() - return { _G.mock_locations[1] } + ['textDocument/definition'] = function(_, _, callback) + callback(nil, { _G.mock_locations[1] }) end, ---@return lsp.WorkspaceSymbol[] - ['workspace/symbol'] = function(_, request) + ['workspace/symbol'] = function(_, request, callback) assert(request.query == 'foobar') - return { + callback(nil, { { name = 'foobar', kind = 13, ---@type lsp.SymbolKind @@ -4148,7 +4711,7 @@ describe('LSP', function() kind = 12, ---@type lsp.SymbolKind location = _G.mock_locations[2], } - } + }) end, }, }) @@ -4221,10 +4784,10 @@ describe('LSP', function() server:shutdown() return vim.json.decode(init) ]] - eq(result.method, 'initialize') + eq('initialize', result.method) end) - it('can connect to lsp server via rpc.domain_socket_connect', function() - local tmpfile + it('can connect to lsp server via pipe or domain_socket', function() + local tmpfile --- @type string if is_os('win') then tmpfile = '\\\\.\\\\pipe\\pipe.test' else @@ -4248,7 +4811,7 @@ describe('LSP', function() client:close() end)) end) - vim.lsp.start({ name = "dummy", cmd = vim.lsp.rpc.domain_socket_connect(SOCK) }) + vim.lsp.start({ name = "dummy", cmd = vim.lsp.rpc.connect(SOCK) }) vim.wait(1000, function() return init ~= nil end) assert(init, "server must receive `initialize` request") server:close() @@ -4257,7 +4820,7 @@ describe('LSP', function() ]], tmpfile ) - eq(result.method, 'initialize') + eq('initialize', result.method) end) end) @@ -4449,18 +5012,26 @@ describe('LSP', function() string.format('sends notifications when files change (watchfunc=%s)', watchfunc), function() if watchfunc == 'fswatch' then + skip(is_os('win'), 'not supported on windows') + skip(is_os('mac'), 'flaky test on mac') skip( not is_ci() and fn.executable('fswatch') == 0, 'fswatch not installed and not on CI' ) - skip(is_os('win'), 'not supported on windows') - skip(is_os('mac'), 'flaky') end - skip( - is_os('bsd'), - 'kqueue only reports events on watched folder itself, not contained files #26110' - ) + if watchfunc == 'watch' then + skip(is_os('mac'), 'flaky test on mac') + skip( + is_os('bsd'), + 'Stopped working on bsd after 3ca967387c49c754561c3b11a574797504d40f38' + ) + else + skip( + is_os('bsd'), + 'kqueue only reports events on watched folder itself, not contained files #26110' + ) + end local root_dir = tmpname() os.remove(root_dir) @@ -4679,14 +5250,7 @@ describe('LSP', function() ) local function watched_uri(fname) - return exec_lua( - [[ - local root_dir, fname = ... - return vim.uri_from_fname(root_dir .. '/' .. fname) - ]], - root_dir, - fname - ) + return vim.uri_from_fname(root_dir .. '/' .. fname) end eq(4, #result) @@ -4792,13 +5356,7 @@ describe('LSP', function() ) local function watched_uri(fname) - return exec_lua( - [[ - local fname = ... - return vim.uri_from_fname('/dir/' .. fname) - ]], - fname - ) + return vim.uri_from_fname('/dir/' .. fname) end eq(3, #result) @@ -4923,30 +5481,21 @@ describe('LSP', function() root_dir ) - local function watched_uri(fname) - return exec_lua( - [[ - return vim.uri_from_fname(...) - ]], - fname - ) - end - eq(3, #result) eq('workspace/didChangeWatchedFiles', result[3].method) eq({ changes = { { type = exec_lua([[return vim.lsp.protocol.FileChangeType.Created]]), - uri = watched_uri('file1'), + uri = vim.uri_from_fname('file1'), }, { type = exec_lua([[return vim.lsp.protocol.FileChangeType.Changed]]), - uri = watched_uri('file1'), + uri = vim.uri_from_fname('file1'), }, { type = exec_lua([[return vim.lsp.protocol.FileChangeType.Created]]), - uri = watched_uri('file2'), + uri = vim.uri_from_fname('file2'), }, }, }, result[3].params) @@ -5009,7 +5558,7 @@ describe('LSP', function() ) end - eq(true, check_registered(nil)) -- start{_client}() defaults to make_client_capabilities(). + eq(is_os('mac') or is_os('win'), check_registered(nil)) -- start{_client}() defaults to make_client_capabilities(). eq(false, check_registered(vim.empty_dict())) eq( false, diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua index 5bfa566729..978178191c 100644 --- a/test/functional/plugin/man_spec.lua +++ b/test/functional/plugin/man_spec.lua @@ -1,17 +1,19 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local command, rawfeed = helpers.command, helpers.rawfeed -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local fn = helpers.fn -local nvim_prog = helpers.nvim_prog -local matches = helpers.matches -local write_file = helpers.write_file -local tmpname = helpers.tmpname -local eq = helpers.eq + +local command, feed = n.command, n.feed +local clear = n.clear +local exec_lua = n.exec_lua +local fn = n.fn +local nvim_prog = n.nvim_prog +local matches = t.matches +local write_file = t.write_file +local tmpname = t.tmpname +local eq = t.eq local pesc = vim.pesc -local skip = helpers.skip -local is_ci = helpers.is_ci +local skip = t.skip +local is_ci = t.is_ci -- Collects all names passed to find_path() after attempting ":Man foo". local function get_search_history(name) @@ -44,7 +46,7 @@ describe(':Man', function() end) describe('man.lua: highlight_line()', function() - local screen + local screen --- @type test.functional.ui.screen before_each(function() command('syntax on') @@ -64,7 +66,7 @@ describe(':Man', function() end) it('clears backspaces from text and adds highlights', function() - rawfeed( + feed( [[ ithis i<C-v><C-h>is<C-v><C-h>s a<C-v><C-h>a test with _<C-v><C-h>o_<C-v><C-h>v_<C-v><C-h>e_<C-v><C-h>r_<C-v><C-h>s_<C-v><C-h>t_<C-v><C-h>r_<C-v><C-h>u_<C-v><C-h>c_<C-v><C-h>k text<ESC>]] @@ -90,7 +92,7 @@ describe(':Man', function() end) it('clears escape sequences from text and adds highlights', function() - rawfeed( + feed( [[ ithis <C-v><ESC>[1mis <C-v><ESC>[3ma <C-v><ESC>[4mtest<C-v><ESC>[0m <C-v><ESC>[4mwith<C-v><ESC>[24m <C-v><ESC>[4mescaped<C-v><ESC>[24m <C-v><ESC>[4mtext<C-v><ESC>[24m<ESC>]] @@ -116,7 +118,7 @@ describe(':Man', function() end) it('highlights multibyte text', function() - rawfeed( + feed( [[ ithis i<C-v><C-h>is<C-v><C-h>s あ<C-v><C-h>あ test with _<C-v><C-h>ö_<C-v><C-h>v_<C-v><C-h>e_<C-v><C-h>r_<C-v><C-h>s_<C-v><C-h>t_<C-v><C-h>r_<C-v><C-h>u_<C-v><C-h>̃_<C-v><C-h>c_<C-v><C-h>k te<C-v><ESC>[3mxt¶<C-v><ESC>[0m<ESC>]] @@ -132,7 +134,7 @@ describe(':Man', function() end) it('highlights underscores based on context', function() - rawfeed( + feed( [[ i_<C-v><C-h>_b<C-v><C-h>be<C-v><C-h>eg<C-v><C-h>gi<C-v><C-h>in<C-v><C-h>ns<C-v><C-h>s m<C-v><C-h>mi<C-v><C-h>id<C-v><C-h>d_<C-v><C-h>_d<C-v><C-h>dl<C-v><C-h>le<C-v><C-h>e @@ -150,7 +152,7 @@ describe(':Man', function() end) it('highlights various bullet formats', function() - rawfeed([[ + feed([[ i· ·<C-v><C-h>· +<C-v><C-h>o +<C-v><C-h>+<C-v><C-h>o<C-v><C-h>o double<ESC>]]) @@ -166,7 +168,7 @@ describe(':Man', function() end) it('handles : characters in input', function() - rawfeed([[ + feed([[ i<C-v><C-[>[40m 0 <C-v><C-[>[41m 1 <C-v><C-[>[42m 2 <C-v><C-[>[43m 3 <C-v><C-[>[44m 4 <C-v><C-[>[45m 5 <C-v><C-[>[46m 6 <C-v><C-[>[47m 7 <C-v><C-[>[100m 8 <C-v><C-[>[101m 9 <C-v><C-[>[102m 10 <C-v><C-[>[103m 11 <C-v><C-[>[104m 12 <C-v><C-[>[105m 13 <C-v><C-[>[106m 14 <C-v><C-[>[107m 15 @@ -190,6 +192,7 @@ describe(':Man', function() '--headless', '+autocmd VimLeave * echo "quit works!!"', '+Man!', + '+tag ls', '+call nvim_input("q")', } matches('quit works!!', fn.system(args, { 'manpage contents' })) diff --git a/test/functional/plugin/matchparen_spec.lua b/test/functional/plugin/matchparen_spec.lua index 530afd16e4..ae718ac1bd 100644 --- a/test/functional/plugin/matchparen_spec.lua +++ b/test/functional/plugin/matchparen_spec.lua @@ -1,14 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local api = helpers.api -local feed = helpers.feed -local eq = helpers.eq +local clear = n.clear +local command = n.command +local api = n.api +local feed = n.feed +local eq = t.eq describe('matchparen', function() - local screen + local screen --- @type test.functional.ui.screen before_each(function() clear { args = { '-u', 'NORC' } } diff --git a/test/functional/plugin/msgpack_spec.lua b/test/functional/plugin/msgpack_spec.lua index 8511e6c703..1d5d20ec02 100644 --- a/test/functional/plugin/msgpack_spec.lua +++ b/test/functional/plugin/msgpack_spec.lua @@ -1,11 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local api = helpers.api -local eq = helpers.eq -local nvim_eval = helpers.eval -local nvim_command = helpers.command -local exc_exec = helpers.exc_exec -local ok = helpers.ok +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local api = n.api +local eq = t.eq +local nvim_eval = n.eval +local nvim_command = n.command +local exc_exec = n.exc_exec +local ok = t.ok local NIL = vim.NIL describe('autoload/msgpack.vim', function() diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index 1c20548321..1c2bcbd497 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear +local t_shada = require('test.functional.shada.testutil') + +local clear = n.clear local eq, api, nvim_eval, nvim_command, exc_exec, fn, nvim_feed = - helpers.eq, helpers.api, helpers.eval, helpers.command, helpers.exc_exec, helpers.fn, helpers.feed -local neq = helpers.neq -local read_file = helpers.read_file + t.eq, n.api, n.eval, n.command, n.exc_exec, n.fn, n.feed +local neq = t.neq +local read_file = t.read_file -local shada_helpers = require('test.functional.shada.helpers') -local get_shada_rw = shada_helpers.get_shada_rw +local get_shada_rw = t_shada.get_shada_rw local function reset(shada_file) clear { args = { '-u', 'NORC', '-i', shada_file or 'NONE' } } @@ -2612,7 +2614,7 @@ end) describe('plugin/shada.vim', function() local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) - local eol = helpers.is_os('win') and '\r\n' or '\n' + local eol = t.is_os('win') and '\r\n' or '\n' before_each(function() -- Note: reset() is called explicitly in each test. os.remove(fname) diff --git a/test/functional/plugin/tohtml_spec.lua b/test/functional/plugin/tohtml_spec.lua index 2ac0fe1fa3..200a5f34b2 100644 --- a/test/functional/plugin/tohtml_spec.lua +++ b/test/functional/plugin/tohtml_spec.lua @@ -1,13 +1,14 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local exec = helpers.exec -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local fn = helpers.fn -local api = helpers.api -local insert = helpers.insert +local clear = n.clear +local exec = n.exec +local exec_lua = n.exec_lua +local eq = t.eq +local fn = n.fn +local api = n.api +local insert = n.insert local function html_syntax_match() local styles = diff --git a/test/functional/plugin/tutor_spec.lua b/test/functional/plugin/tutor_spec.lua index 99538e1db0..9f381d45db 100644 --- a/test/functional/plugin/tutor_spec.lua +++ b/test/functional/plugin/tutor_spec.lua @@ -1,18 +1,20 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local feed = helpers.feed -local is_os = helpers.is_os + +local clear = n.clear +local command = n.command +local feed = n.feed +local is_os = t.is_os describe(':Tutor', function() - local screen + local screen --- @type test.functional.ui.screen before_each(function() clear({ args = { '--clean' } }) command('set cmdheight=0') command('Tutor') - screen = Screen.new(80, 30) + screen = Screen.new(81, 30) screen:set_default_attr_ids({ [0] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray }, [1] = { bold = true }, @@ -20,6 +22,7 @@ describe(':Tutor', function() [3] = { foreground = Screen.colors.SlateBlue }, [4] = { bold = true, foreground = Screen.colors.Brown }, [5] = { bold = true, foreground = Screen.colors.Magenta1 }, + [6] = { italic = true }, }) screen:attach() end) @@ -27,71 +30,109 @@ describe(':Tutor', function() it('applies {unix:…,win:…} transform', function() local expected = is_os('win') and [[ - {0: }^ | - {0: } 3. To verify that a file was retrieved, cursor back and notice that there | - {0: } are now two copies of Lesson 5.3, the original and the retrieved version. | - {0: } | - {0: }{1:NOTE}: You can also read the output of an external command. For example, | - {0: } | - {0: } :r {4:!}dir | - {0: } | - {0: } reads the output of the ls command and puts it below the cursor. | - {0: } | - {0: }{3:#}{5: Lesson 5 SUMMARY} | - {0: } | - {0: } 1. {2::!command} executes an external command. | - {0: } | - {0: } Some useful examples are: | - {0: } :{4:!}dir - shows a directory listing | - {0: } :{4:!}del FILENAME - removes file FILENAME | - {0: } | - {0: } 2. {2::w} FILENAME writes the current Neovim file to disk with | - {0: } name FILENAME. | - {0: } | - {0: } 3. {2:v} motion :w FILENAME saves the Visually selected lines in file | - {0: } FILENAME. | - {0: } | - {0: } 4. {2::r} FILENAME retrieves disk file FILENAME and puts it | - {0: } below the cursor position. | - {0: } | - {0: } 5. {2::r !dir} reads the output of the dir command and | - {0: } puts it below the cursor position. | - {0: } | + {0: }^ | + {0: } 3. To verify that a file was retrieved, cursor back and notice that there | + {0: } are now two copies of Lesson 5.3, the original and the retrieved version. | + {0: } | + {0: }{1:NOTE}: You can also read the output of an external command. For example, | + {0: } | + {0: } :r {4:!}dir | + {0: } | + {0: } reads the output of the ls command and puts it below the cursor. | + {0: } | + {0: }{3:#}{5: Lesson 5 SUMMARY} | + {0: } | + {0: } 1. {2::!command} executes an external command. | + {0: } | + {0: } Some useful examples are: | + {0: } :{4:!}dir - shows a directory listing | + {0: } :{4:!}del FILENAME - removes file FILENAME | + {0: } | + {0: } 2. {2::w} FILENAME writes the current Neovim file to disk with | + {0: } name FILENAME. | + {0: } | + {0: } 3. {2:v} motion :w FILENAME saves the Visually selected lines in file | + {0: } FILENAME. | + {0: } | + {0: } 4. {2::r} FILENAME retrieves disk file FILENAME and puts it | + {0: } below the cursor position. | + {0: } | + {0: } 5. {2::r !dir} reads the output of the dir command and | + {0: } puts it below the cursor position. | + {0: } | ]] or [[ - {0: }^ | - {0: } 3. To verify that a file was retrieved, cursor back and notice that there | - {0: } are now two copies of Lesson 5.3, the original and the retrieved version. | - {0: } | - {0: }{1:NOTE}: You can also read the output of an external command. For example, | - {0: } | - {0: } :r {4:!}ls | - {0: } | - {0: } reads the output of the ls command and puts it below the cursor. | - {0: } | - {0: }{3:#}{5: Lesson 5 SUMMARY} | - {0: } | - {0: } 1. {2::!command} executes an external command. | - {0: } | - {0: } Some useful examples are: | - {0: } :{4:!}ls - shows a directory listing | - {0: } :{4:!}rm FILENAME - removes file FILENAME | - {0: } | - {0: } 2. {2::w} FILENAME writes the current Neovim file to disk with | - {0: } name FILENAME. | - {0: } | - {0: } 3. {2:v} motion :w FILENAME saves the Visually selected lines in file | - {0: } FILENAME. | - {0: } | - {0: } 4. {2::r} FILENAME retrieves disk file FILENAME and puts it | - {0: } below the cursor position. | - {0: } | - {0: } 5. {2::r !ls} reads the output of the ls command and | - {0: } puts it below the cursor position. | - {0: } | + {0: }^ | + {0: } 3. To verify that a file was retrieved, cursor back and notice that there | + {0: } are now two copies of Lesson 5.3, the original and the retrieved version. | + {0: } | + {0: }{1:NOTE}: You can also read the output of an external command. For example, | + {0: } | + {0: } :r {4:!}ls | + {0: } | + {0: } reads the output of the ls command and puts it below the cursor. | + {0: } | + {0: }{3:#}{5: Lesson 5 SUMMARY} | + {0: } | + {0: } 1. {2::!command} executes an external command. | + {0: } | + {0: } Some useful examples are: | + {0: } :{4:!}ls - shows a directory listing | + {0: } :{4:!}rm FILENAME - removes file FILENAME | + {0: } | + {0: } 2. {2::w} FILENAME writes the current Neovim file to disk with | + {0: } name FILENAME. | + {0: } | + {0: } 3. {2:v} motion :w FILENAME saves the Visually selected lines in file | + {0: } FILENAME. | + {0: } | + {0: } 4. {2::r} FILENAME retrieves disk file FILENAME and puts it | + {0: } below the cursor position. | + {0: } | + {0: } 5. {2::r !ls} reads the output of the ls command and | + {0: } puts it below the cursor position. | + {0: } | ]] feed(':700<CR>zt') screen:expect(expected) end) + + it('applies hyperlink highlighting', function() + local expected = [[ + {0: }^ | + {0: }{3:#}{5: CONCLUSION} | + {0: } | + {0: }This was intended to give a brief overview of the Neovim editor, just enough to| + {0: }allow you to use it fairly easily. It is far from complete as Neovim has | + {0: }many many more commands. Consult the help often. | + {0: }There are also countless great tutorials and videos to be found online. | + {0: }Here's a bunch of them: | + {0: } | + {0: }- {6:Learn Vim Progressively}: | + {0: } {2:https://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/} | + {0: }- {6:Learning Vim in 2014}: | + {0: } {2:https://benmccormick.org/learning-vim-in-2014/} | + {0: }- {6:Vimcasts}: | + {0: } {2:http://vimcasts.org/} | + {0: }- {6:Vim Video-Tutorials by Derek Wyatt}: | + {0: } {2:http://derekwyatt.org/vim/tutorials/} | + {0: }- {6:Learn Vimscript the Hard Way}: | + {0: } {2:https://learnvimscriptthehardway.stevelosh.com/} | + {0: }- {6:7 Habits of Effective Text Editing}: | + {0: } {2:https://www.moolenaar.net/habits.html} | + {0: }- {6:vim-galore}: | + {0: } {2:https://github.com/mhinz/vim-galore} | + {0: } | + {0: }If you prefer a book, {6:Practical Vim} by Drew Neil is recommended often | + {0: }(the sequel, {6:Modern Vim}, includes material specific to Neovim). | + {0: } | + {0: }This tutorial was written by Michael C. Pierce and Robert K. Ware, Colorado | + {0: }School of Mines using ideas supplied by Charles Smith, Colorado State | + {0: }University. E-mail: {2:bware@mines.colorado.edu}. | + ]] + + feed(':960<CR>zt') + screen:expect(expected) + end) end) diff --git a/test/functional/plugin/vim_syntax_spec.lua b/test/functional/plugin/vim_syntax_spec.lua index 9396bbce5c..d99a69eab9 100644 --- a/test/functional/plugin/vim_syntax_spec.lua +++ b/test/functional/plugin/vim_syntax_spec.lua @@ -1,15 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local exec = helpers.exec -local api = helpers.api + +local clear = n.clear +local exec = n.exec +local api = n.api describe('Vimscript syntax highlighting', function() - local screen + local screen --- @type test.functional.ui.screen before_each(function() clear() - helpers.add_builddir_to_rtp() + n.add_builddir_to_rtp() exec([[ setfiletype vim syntax on |