From 844cd9cef12d77fd5d0bb14819dbe201dbf68f0b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 8 Jan 2020 09:20:53 -0800 Subject: test: just say no to hyper-granularity - Move plugin/lsp/* to plugin/* - Merge lsp/util_spec.lua into lsp_spec.lua --- test/functional/plugin/lsp_spec.lua | 776 ++++++++++++++++++++++++++++++++++++ 1 file changed, 776 insertions(+) create mode 100644 test/functional/plugin/lsp_spec.lua (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua new file mode 100644 index 0000000000..803483c0ef --- /dev/null +++ b/test/functional/plugin/lsp_spec.lua @@ -0,0 +1,776 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local dedent = helpers.dedent +local exec_lua = helpers.exec_lua +local eq = helpers.eq +local insert = helpers.insert +local iswin = helpers.iswin +local retry = helpers.retry +local NIL = helpers.NIL + +-- Use these to get access to a coroutine so that I can run async tests and use +-- yield. +local run, stop = helpers.run, helpers.stop + +if helpers.pending_win32(pending) then return end + +local lsp_test_rpc_server_file = "test/functional/fixtures/lsp-test-rpc-server.lua" +if iswin() then + lsp_test_rpc_server_file = lsp_test_rpc_server_file:gsub("/", "\\") +end + +local function test_rpc_server_setup(test_name, timeout_ms) + exec_lua([=[ + lsp = require('vim.lsp') + local test_name, fixture_filename, timeout = ... + TEST_RPC_CLIENT_ID = lsp.start_client { + cmd = { + vim.api.nvim_get_vvar("progpath"), '-Es', '-u', 'NONE', '--headless', + "-c", string.format("lua TEST_NAME = %q", test_name), + "-c", string.format("lua TIMEOUT = %d", timeout), + "-c", "luafile "..fixture_filename, + }; + callbacks = setmetatable({}, { + __index = function(t, method) + return function(...) + return vim.rpcrequest(1, 'callback', ...) + end + end; + }); + root_dir = vim.loop.cwd(); + on_init = function(client, result) + TEST_RPC_CLIENT = client + vim.rpcrequest(1, "init", result) + end; + on_exit = function(...) + vim.rpcnotify(1, "exit", ...) + end; + } + ]=], test_name, lsp_test_rpc_server_file, timeout_ms or 1e3) +end + +local function test_rpc_server(config) + if config.test_name then + clear() + test_rpc_server_setup(config.test_name, config.timeout_ms or 1e3) + end + local client = setmetatable({}, { + __index = function(_, name) + -- Workaround for not being able to yield() inside __index for Lua 5.1 :( + -- Otherwise I would just return the value here. + return function(...) + return exec_lua([=[ + local name = ... + if type(TEST_RPC_CLIENT[name]) == 'function' then + return TEST_RPC_CLIENT[name](select(2, ...)) + else + return TEST_RPC_CLIENT[name] + end + ]=], name, ...) + end + end; + }) + local code, signal + local function on_request(method, args) + if method == "init" then + if config.on_init then + config.on_init(client, unpack(args)) + end + return NIL + end + if method == 'callback' then + if config.on_callback then + config.on_callback(unpack(args)) + end + end + return NIL + end + local function on_notify(method, args) + if method == 'exit' then + code, signal = unpack(args) + return stop() + end + end + -- TODO specify timeout? + -- run(on_request, on_notify, config.on_setup, 1000) + run(on_request, on_notify, config.on_setup) + if config.on_exit then + config.on_exit(code, signal) + end + stop() + if config.test_name then + exec_lua("lsp._vim_exit_handler()") + end +end + +describe('LSP', function() + describe('server_name specified', function() + before_each(function() + clear() + -- Run an instance of nvim on the file which contains our "scripts". + -- Pass TEST_NAME to pick the script. + local test_name = "basic_init" + exec_lua([=[ + lsp = require('vim.lsp') + local test_name, fixture_filename = ... + function test__start_client() + return lsp.start_client { + cmd = { + vim.api.nvim_get_vvar("progpath"), '-Es', '-u', 'NONE', '--headless', + "-c", string.format("lua TEST_NAME = %q", test_name), + "-c", "luafile "..fixture_filename; + }; + root_dir = vim.loop.cwd(); + } + end + TEST_CLIENT1 = test__start_client() + ]=], test_name, lsp_test_rpc_server_file) + end) + + after_each(function() + exec_lua("lsp._vim_exit_handler()") + -- exec_lua("lsp.stop_all_clients(true)") + end) + + it('start_client(), stop_client()', function() + retry(nil, 4000, function() + eq(1, exec_lua('return #lsp.get_active_clients()')) + end) + eq(2, exec_lua([[ + TEST_CLIENT2 = test__start_client() + return TEST_CLIENT2 + ]])) + eq(3, exec_lua([[ + TEST_CLIENT3 = test__start_client() + return TEST_CLIENT3 + ]])) + retry(nil, 4000, function() + eq(3, exec_lua('return #lsp.get_active_clients()')) + end) + + eq(false, exec_lua('return lsp.get_client_by_id(TEST_CLIENT1) == nil')) + eq(false, exec_lua('return lsp.get_client_by_id(TEST_CLIENT1).is_stopped()')) + exec_lua('return lsp.get_client_by_id(TEST_CLIENT1).stop()') + retry(nil, 4000, function() + eq(2, exec_lua('return #lsp.get_active_clients()')) + end) + eq(true, exec_lua('return lsp.get_client_by_id(TEST_CLIENT1) == nil')) + + exec_lua('lsp.stop_client({TEST_CLIENT2, TEST_CLIENT3})') + retry(nil, 4000, function() + eq(0, exec_lua('return #lsp.get_active_clients()')) + end) + end) + + it('stop_client() also works on client objects', function() + exec_lua([[ + TEST_CLIENT2 = test__start_client() + TEST_CLIENT3 = test__start_client() + ]]) + retry(nil, 4000, function() + eq(3, exec_lua('return #lsp.get_active_clients()')) + end) + -- Stop all clients. + exec_lua('lsp.stop_client(lsp.get_active_clients())') + retry(nil, 4000, function() + eq(0, exec_lua('return #lsp.get_active_clients()')) + end) + end) + end) + + describe('basic_init test', function() + it('should run correctly', function() + local expected_callbacks = { + {NIL, "test", {}, 1}; + } + test_rpc_server { + test_name = "basic_init"; + on_init = function(client, _init_result) + -- client is a dummy object which will queue up commands to be run + -- once the server initializes. It can't accept lua callbacks or + -- other types that may be unserializable for now. + client.stop() + end; + -- If the program timed out, then code will be nil. + on_exit = function(code, signal) + eq(0, code, "exit code") eq(0, signal, "exit signal") + end; + -- Note that NIL must be used here. + -- on_callback(err, method, result, client_id) + on_callback = function(...) + eq(table.remove(expected_callbacks), {...}) + end; + } + end) + + it('should fail', function() + local expected_callbacks = { + {NIL, "test", {}, 1}; + } + test_rpc_server { + test_name = "basic_init"; + on_init = function(client) + client.notify('test') + client.stop() + end; + on_exit = function(code, signal) + eq(1, code, "exit code") eq(0, signal, "exit signal") + end; + on_callback = function(...) + eq(table.remove(expected_callbacks), {...}, "expected callback") + end; + } + end) + + it('should succeed with manual shutdown', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + {NIL, "test", {}, 1}; + } + test_rpc_server { + test_name = "basic_init"; + on_init = function(client) + eq(0, client.resolved_capabilities().text_document_did_change) + client.request('shutdown') + client.notify('exit') + end; + on_exit = function(code, signal) + eq(0, code, "exit code") eq(0, signal, "exit signal") + end; + on_callback = function(...) + eq(table.remove(expected_callbacks), {...}, "expected callback") + end; + } + end) + + it('should verify capabilities sent', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + } + test_rpc_server { + test_name = "basic_check_capabilities"; + on_init = function(client) + client.stop() + end; + on_exit = function(code, signal) + eq(0, code, "exit code") eq(0, signal, "exit signal") + end; + on_callback = function(...) + eq(table.remove(expected_callbacks), {...}, "expected callback") + end; + } + end) + + it('should not send didOpen if the buffer closes before init', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + {NIL, "finish", {}, 1}; + } + local client + test_rpc_server { + test_name = "basic_finish"; + on_setup = function() + exec_lua [[ + BUFFER = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, { + "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") + ]] + end; + on_init = function(_client) + client = _client + local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full") + eq(full_kind, client.resolved_capabilities().text_document_did_change) + eq(true, client.resolved_capabilities().text_document_open_close) + client.notify('finish') + end; + on_exit = function(code, signal) + eq(0, code, "exit code") eq(0, signal, "exit signal") + end; + on_callback = function(err, method, params, client_id) + eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") + if method == 'finish' then + client.stop() + end + end; + } + end) + + it('should check the body sent attaching before init', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + {NIL, "finish", {}, 1}; + {NIL, "start", {}, 1}; + } + local client + test_rpc_server { + test_name = "basic_check_buffer_open"; + on_setup = function() + exec_lua [[ + BUFFER = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, { + "testing"; + "123"; + }) + ]] + exec_lua [[ + assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)) + ]] + end; + on_init = function(_client) + client = _client + local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full") + eq(full_kind, client.resolved_capabilities().text_document_did_change) + eq(true, client.resolved_capabilities().text_document_open_close) + exec_lua [[ + assert(not lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID), "Shouldn't attach twice") + ]] + end; + on_exit = function(code, signal) + eq(0, code, "exit code") eq(0, signal, "exit signal") + end; + on_callback = function(err, method, params, client_id) + if method == 'start' then + client.notify('finish') + end + eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") + if method == 'finish' then + client.stop() + end + end; + } + end) + + it('should check the body sent attaching after init', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + {NIL, "finish", {}, 1}; + {NIL, "start", {}, 1}; + } + local client + test_rpc_server { + test_name = "basic_check_buffer_open"; + on_setup = function() + exec_lua [[ + BUFFER = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, { + "testing"; + "123"; + }) + ]] + end; + on_init = function(_client) + client = _client + local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full") + eq(full_kind, client.resolved_capabilities().text_document_did_change) + eq(true, client.resolved_capabilities().text_document_open_close) + exec_lua [[ + assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)) + ]] + end; + on_exit = function(code, signal) + eq(0, code, "exit code") eq(0, signal, "exit signal") + end; + on_callback = function(err, method, params, client_id) + if method == 'start' then + client.notify('finish') + end + eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") + if method == 'finish' then + client.stop() + end + end; + } + end) + + it('should check the body and didChange full', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + {NIL, "finish", {}, 1}; + {NIL, "start", {}, 1}; + } + local client + test_rpc_server { + test_name = "basic_check_buffer_open_and_change"; + on_setup = function() + exec_lua [[ + BUFFER = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, { + "testing"; + "123"; + }) + ]] + end; + on_init = function(_client) + client = _client + local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full") + eq(full_kind, client.resolved_capabilities().text_document_did_change) + eq(true, client.resolved_capabilities().text_document_open_close) + exec_lua [[ + assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)) + ]] + end; + on_exit = function(code, signal) + eq(0, code, "exit code") eq(0, signal, "exit signal") + end; + on_callback = function(err, method, params, client_id) + if method == 'start' then + exec_lua [[ + vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { + "boop"; + }) + ]] + client.notify('finish') + end + eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") + if method == 'finish' then + client.stop() + end + end; + } + end) + + it('should check the body and didChange full with noeol', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + {NIL, "finish", {}, 1}; + {NIL, "start", {}, 1}; + } + local client + test_rpc_server { + test_name = "basic_check_buffer_open_and_change_noeol"; + on_setup = function() + exec_lua [[ + BUFFER = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, { + "testing"; + "123"; + }) + vim.api.nvim_buf_set_option(BUFFER, 'eol', false) + ]] + end; + on_init = function(_client) + client = _client + local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full") + eq(full_kind, client.resolved_capabilities().text_document_did_change) + eq(true, client.resolved_capabilities().text_document_open_close) + exec_lua [[ + assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)) + ]] + end; + on_exit = function(code, signal) + eq(0, code, "exit code") eq(0, signal, "exit signal") + end; + on_callback = function(err, method, params, client_id) + if method == 'start' then + exec_lua [[ + vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { + "boop"; + }) + ]] + client.notify('finish') + end + eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") + if method == 'finish' then + client.stop() + end + end; + } + end) + + -- TODO(askhan) we don't support full for now, so we can disable these tests. + pending('should check the body and didChange incremental', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + {NIL, "finish", {}, 1}; + {NIL, "start", {}, 1}; + } + local client + test_rpc_server { + test_name = "basic_check_buffer_open_and_change_incremental"; + on_setup = function() + exec_lua [[ + BUFFER = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, { + "testing"; + "123"; + }) + ]] + end; + on_init = function(_client) + client = _client + local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Incremental") + eq(sync_kind, client.resolved_capabilities().text_document_did_change) + eq(true, client.resolved_capabilities().text_document_open_close) + exec_lua [[ + assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)) + ]] + end; + on_exit = function(code, signal) + eq(0, code, "exit code") eq(0, signal, "exit signal") + end; + on_callback = function(err, method, params, client_id) + if method == 'start' then + exec_lua [[ + vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { + "boop"; + }) + ]] + client.notify('finish') + end + eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") + if method == 'finish' then + client.stop() + end + end; + } + end) + + -- TODO(askhan) we don't support full for now, so we can disable these tests. + pending('should check the body and didChange incremental normal mode editting', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + {NIL, "finish", {}, 1}; + {NIL, "start", {}, 1}; + } + local client + test_rpc_server { + test_name = "basic_check_buffer_open_and_change_incremental_editting"; + on_setup = function() + exec_lua [[ + BUFFER = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, { + "testing"; + "123"; + }) + ]] + end; + on_init = function(_client) + client = _client + local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Incremental") + eq(sync_kind, client.resolved_capabilities().text_document_did_change) + eq(true, client.resolved_capabilities().text_document_open_close) + exec_lua [[ + assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)) + ]] + end; + on_exit = function(code, signal) + eq(0, code, "exit code") eq(0, signal, "exit signal") + end; + on_callback = function(err, method, params, client_id) + if method == 'start' then + helpers.command("normal! 1Go") + client.notify('finish') + end + eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") + if method == 'finish' then + client.stop() + end + end; + } + end) + + it('should check the body and didChange full with 2 changes', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + {NIL, "finish", {}, 1}; + {NIL, "start", {}, 1}; + } + local client + test_rpc_server { + test_name = "basic_check_buffer_open_and_change_multi"; + on_setup = function() + exec_lua [[ + BUFFER = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, { + "testing"; + "123"; + }) + ]] + end; + on_init = function(_client) + client = _client + local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full") + eq(sync_kind, client.resolved_capabilities().text_document_did_change) + eq(true, client.resolved_capabilities().text_document_open_close) + exec_lua [[ + assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)) + ]] + end; + on_exit = function(code, signal) + eq(0, code, "exit code") eq(0, signal, "exit signal") + end; + on_callback = function(err, method, params, client_id) + if method == 'start' then + exec_lua [[ + vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { + "321"; + }) + vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { + "boop"; + }) + ]] + client.notify('finish') + end + eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") + if method == 'finish' then + client.stop() + end + end; + } + end) + + it('should check the body and didChange full lifecycle', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + {NIL, "finish", {}, 1}; + {NIL, "start", {}, 1}; + } + local client + test_rpc_server { + test_name = "basic_check_buffer_open_and_change_multi_and_close"; + on_setup = function() + exec_lua [[ + BUFFER = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, { + "testing"; + "123"; + }) + ]] + end; + on_init = function(_client) + client = _client + local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full") + eq(sync_kind, client.resolved_capabilities().text_document_did_change) + eq(true, client.resolved_capabilities().text_document_open_close) + exec_lua [[ + assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)) + ]] + end; + on_exit = function(code, signal) + eq(0, code, "exit code") eq(0, signal, "exit signal") + end; + on_callback = function(err, method, params, client_id) + if method == 'start' then + exec_lua [[ + vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { + "321"; + }) + vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { + "boop"; + }) + vim.api.nvim_command(BUFFER.."bwipeout") + ]] + client.notify('finish') + end + eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") + if method == 'finish' then + client.stop() + end + end; + } + end) + + end) + + describe("parsing tests", function() + it('should handle invalid content-length correctly', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + {NIL, "finish", {}, 1}; + {NIL, "start", {}, 1}; + } + local client + test_rpc_server { + test_name = "invalid_header"; + on_setup = function() + end; + on_init = function(_client) + client = _client + client.stop(true) + end; + on_exit = function(code, signal) + eq(0, code, "exit code") eq(0, signal, "exit signal") + end; + on_callback = function(err, method, params, client_id) + eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") + end; + } + end) + + end) +end) + +describe('LSP util', function() + before_each(function() + clear() + insert(dedent([[ + First line of text + Second line of text + Third line of text + Fourth line of text]])) + end) + + local function make_edit(y_0, x_0, y_1, x_1, text) + return { + range = { + start = { line = y_0, character = x_0 }; + ["end"] = { line = y_1, character = x_1 }; + }; + newText = type(text) == 'table' and table.concat(text, '\n') or (text or ""); + } + end + + local function buf_lines(bufnr) + return exec_lua("return vim.api.nvim_buf_get_lines((...), 0, -1, false)", bufnr) + end + + describe('apply_edits', function() + it('should apply simple edits', function() + local edits = { + make_edit(0, 0, 0, 0, {"123"}); + make_edit(1, 0, 1, 1, {"2"}); + make_edit(2, 0, 2, 2, {"3"}); + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + eq({ + '123First line of text'; + '2econd line of text'; + '3ird line of text'; + 'Fourth line of text'; + }, buf_lines(1)) + end) + + it('should apply complex edits', function() + local edits = { + make_edit(0, 0, 0, 0, {"", "12"}); + make_edit(0, 0, 0, 0, {"3", "foo"}); + make_edit(0, 1, 0, 1, {"bar", "123"}); + make_edit(0, #"First ", 0, #"First line of text", {"guy"}); + make_edit(1, 0, 1, #'Second', {"baz"}); + make_edit(2, #'Th', 2, #"Third", {"e next"}); + make_edit(3, #'', 3, #"Fourth", {"another line of text", "before this"}); + make_edit(3, #'Fourth', 3, #"Fourth line of text", {"!"}); + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + eq({ + ''; + '123'; + 'fooFbar'; + '123irst guy'; + 'baz line of text'; + 'The next line of text'; + 'another line of text'; + 'before this!'; + }, buf_lines(1)) + end) + end) +end) -- cgit From 8c8681d594a10c7056d62d09267190b6fab37e85 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 8 Jan 2020 09:32:49 -0800 Subject: test: hoist buf_lines() --- test/functional/plugin/lsp_spec.lua | 4 ---- 1 file changed, 4 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 803483c0ef..45452c0b20 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -729,10 +729,6 @@ describe('LSP util', function() } end - local function buf_lines(bufnr) - return exec_lua("return vim.api.nvim_buf_get_lines((...), 0, -1, false)", bufnr) - end - describe('apply_edits', function() it('should apply simple edits', function() local edits = { -- cgit From 0a1c6d9a374a0c984515d0af43b1c71af6c55eb2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 8 Jan 2020 09:46:25 -0800 Subject: LSP: highlight groups test, doc --- test/functional/plugin/lsp_spec.lua | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 45452c0b20..4829a33861 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear +local buf_lines = helpers.buf_lines local dedent = helpers.dedent local exec_lua = helpers.exec_lua local eq = helpers.eq @@ -709,14 +710,9 @@ describe('LSP', function() end) end) -describe('LSP util', function() +describe('LSP', function() before_each(function() clear() - insert(dedent([[ - First line of text - Second line of text - Third line of text - Fourth line of text]])) end) local function make_edit(y_0, x_0, y_1, x_1, text) @@ -729,8 +725,29 @@ describe('LSP util', function() } end + it('highlight groups', function() + eq({'LspDiagnosticsError', + 'LspDiagnosticsHint', + 'LspDiagnosticsInformation', + 'LspDiagnosticsUnderline', + 'LspDiagnosticsUnderlineError', + 'LspDiagnosticsUnderlineHint', + 'LspDiagnosticsUnderlineInformation', + 'LspDiagnosticsUnderlineWarning', + 'LspDiagnosticsWarning', + }, + exec_lua([[require'vim.lsp'; return vim.fn.getcompletion('Lsp', 'highlight')]])) + end) + describe('apply_edits', function() - it('should apply simple edits', function() + before_each(function() + insert(dedent([[ + First line of text + Second line of text + Third line of text + Fourth line of text]])) + end) + it('applies apply simple edits', function() local edits = { make_edit(0, 0, 0, 0, {"123"}); make_edit(1, 0, 1, 1, {"2"}); @@ -744,8 +761,7 @@ describe('LSP util', function() 'Fourth line of text'; }, buf_lines(1)) end) - - it('should apply complex edits', function() + it('applies complex edits', function() local edits = { make_edit(0, 0, 0, 0, {"", "12"}); make_edit(0, 0, 0, 0, {"3", "foo"}); -- cgit From b04165859d0c11e2bdcacd50efb0ec6c2b2a6c0c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 8 Feb 2020 15:35:28 -0800 Subject: test: style --- test/functional/plugin/lsp_spec.lua | 62 +++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 24 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 4829a33861..1db9bf306d 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -16,18 +16,18 @@ local run, stop = helpers.run, helpers.stop if helpers.pending_win32(pending) then return end -local lsp_test_rpc_server_file = "test/functional/fixtures/lsp-test-rpc-server.lua" +local fake_lsp_server_file = "test/functional/fixtures/lsp-test-rpc-server.lua" if iswin() then - lsp_test_rpc_server_file = lsp_test_rpc_server_file:gsub("/", "\\") + fake_lsp_server_file = fake_lsp_server_file:gsub("/", "\\") end -local function test_rpc_server_setup(test_name, timeout_ms) +local function fake_lsp_server_setup(test_name, timeout_ms) exec_lua([=[ lsp = require('vim.lsp') local test_name, fixture_filename, timeout = ... TEST_RPC_CLIENT_ID = lsp.start_client { cmd = { - vim.api.nvim_get_vvar("progpath"), '-Es', '-u', 'NONE', '--headless', + vim.v.progpath, '-Es', '-u', 'NONE', '--headless', "-c", string.format("lua TEST_NAME = %q", test_name), "-c", string.format("lua TIMEOUT = %d", timeout), "-c", "luafile "..fixture_filename, @@ -48,13 +48,13 @@ local function test_rpc_server_setup(test_name, timeout_ms) vim.rpcnotify(1, "exit", ...) end; } - ]=], test_name, lsp_test_rpc_server_file, timeout_ms or 1e3) + ]=], test_name, fake_lsp_server_file, timeout_ms or 1e3) end local function test_rpc_server(config) if config.test_name then clear() - test_rpc_server_setup(config.test_name, config.timeout_ms or 1e3) + fake_lsp_server_setup(config.test_name, config.timeout_ms or 1e3) end local client = setmetatable({}, { __index = function(_, name) @@ -118,7 +118,7 @@ describe('LSP', function() function test__start_client() return lsp.start_client { cmd = { - vim.api.nvim_get_vvar("progpath"), '-Es', '-u', 'NONE', '--headless', + vim.v.progpath, '-Es', '-u', 'NONE', '--headless', "-c", string.format("lua TEST_NAME = %q", test_name), "-c", "luafile "..fixture_filename; }; @@ -126,7 +126,7 @@ describe('LSP', function() } end TEST_CLIENT1 = test__start_client() - ]=], test_name, lsp_test_rpc_server_file) + ]=], test_name, fake_lsp_server_file) end) after_each(function() @@ -195,7 +195,8 @@ describe('LSP', function() end; -- If the program timed out, then code will be nil. on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; -- Note that NIL must be used here. -- on_callback(err, method, result, client_id) @@ -216,7 +217,8 @@ describe('LSP', function() client.stop() end; on_exit = function(code, signal) - eq(1, code, "exit code") eq(0, signal, "exit signal") + eq(1, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") @@ -237,7 +239,8 @@ describe('LSP', function() client.notify('exit') end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") @@ -255,7 +258,8 @@ describe('LSP', function() client.stop() end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") @@ -294,7 +298,8 @@ describe('LSP', function() client.notify('finish') end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") @@ -336,7 +341,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -378,7 +384,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -420,7 +427,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -468,7 +476,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -516,7 +525,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -536,7 +546,7 @@ describe('LSP', function() end) -- TODO(askhan) we don't support full for now, so we can disable these tests. - pending('should check the body and didChange incremental normal mode editting', function() + pending('should check the body and didChange incremental normal mode editing', function() local expected_callbacks = { {NIL, "shutdown", {}, 1}; {NIL, "finish", {}, 1}; @@ -544,7 +554,7 @@ describe('LSP', function() } local client test_rpc_server { - test_name = "basic_check_buffer_open_and_change_incremental_editting"; + test_name = "basic_check_buffer_open_and_change_incremental_editing"; on_setup = function() exec_lua [[ BUFFER = vim.api.nvim_create_buf(false, true) @@ -564,7 +574,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -607,7 +618,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -657,7 +669,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -699,7 +712,8 @@ describe('LSP', function() client.stop(true) end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") -- cgit From c15bd6cd279dbed5d246af05c4c0625387be02af Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 8 Feb 2020 17:16:43 -0800 Subject: test/LSP: use less-generic exit code - os.exit(1) is too generic, since code 1 may be caused by Nvim exiting for some other reason. Change it to os.exit(101). - style: de-architect json_encode/json_decode calls. Failure seen in travis macOS job: https://travis-ci.org/neovim/neovim/jobs/647849133 [ FAILED ] test/functional/plugin/lsp_spec.lua@ 266 SP basic_init test should not send didOpen if the buffer closes before init test/functional/plugin/lsp_spec.lua:297: exit code Expected objects to be the same. Passed in: (number) 1 Expected: (number) 0 stack traceback: test/functional/plugin/lsp_spec.lua:297: in function 'on_exit' test/functional/plugin/lsp_spec.lua:100: in function 'test_rpc_server' test/functional/plugin/lsp_spec.lua:272: in function --- test/functional/plugin/lsp_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 1db9bf306d..cab1fb0d79 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -217,7 +217,7 @@ describe('LSP', function() client.stop() end; on_exit = function(code, signal) - eq(1, code, "exit code") + eq(101, code, "exit code") eq(0, signal, "exit signal") end; on_callback = function(...) -- cgit From 1eb0f5371ae8cee90b97f586a99505cfa5913504 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 8 Feb 2020 17:25:53 -0800 Subject: LSP: fix validate_client_config - `cmd_env` is a table not a function. - tests: Set $NVIM_LOG_FILE for fake LSP server. --- test/functional/plugin/lsp_spec.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index cab1fb0d79..c384fdedf3 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -16,16 +16,16 @@ local run, stop = helpers.run, helpers.stop if helpers.pending_win32(pending) then return end -local fake_lsp_server_file = "test/functional/fixtures/lsp-test-rpc-server.lua" -if iswin() then - fake_lsp_server_file = fake_lsp_server_file:gsub("/", "\\") -end +local fake_lsp_server_file = 'test/functional/fixtures/fake-lsp-server.lua' local function fake_lsp_server_setup(test_name, timeout_ms) exec_lua([=[ lsp = require('vim.lsp') local test_name, fixture_filename, timeout = ... TEST_RPC_CLIENT_ID = lsp.start_client { + cmd_env = { + NVIM_LOG_FILE = 'Xtest-fake-lsp-server.log' + }; cmd = { vim.v.progpath, '-Es', '-u', 'NONE', '--headless', "-c", string.format("lua TEST_NAME = %q", test_name), @@ -117,6 +117,9 @@ describe('LSP', function() local test_name, fixture_filename = ... function test__start_client() return lsp.start_client { + cmd_env = { + NVIM_LOG_FILE = 'Xtest-fake-lsp-server.log' + }; cmd = { vim.v.progpath, '-Es', '-u', 'NONE', '--headless', "-c", string.format("lua TEST_NAME = %q", test_name), @@ -217,7 +220,7 @@ describe('LSP', function() client.stop() end; on_exit = function(code, signal) - eq(101, code, "exit code") + eq(101, code, "exit code") -- See fake-lsp-server.lua eq(0, signal, "exit signal") end; on_callback = function(...) -- cgit From 4cf48dc329e3e3f451ecf902af9af6317d44bb40 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 8 Feb 2020 18:08:02 -0800 Subject: test/LSP: dump logs on error This will help debug CI flakey failures. TODO: helpers.assert_log() -- Explicitly check contents of the logfile. --- test/functional/plugin/lsp_spec.lua | 79 ++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 36 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index c384fdedf3..b2d00a400a 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -5,8 +5,8 @@ local buf_lines = helpers.buf_lines local dedent = helpers.dedent local exec_lua = helpers.exec_lua local eq = helpers.eq +local eq_dumplog = helpers.eq_dumplog local insert = helpers.insert -local iswin = helpers.iswin local retry = helpers.retry local NIL = helpers.NIL @@ -14,17 +14,24 @@ local NIL = helpers.NIL -- yield. local run, stop = helpers.run, helpers.stop +-- TODO(justinmk): hangs on Windows https://github.com/neovim/neovim/pull/11837 if helpers.pending_win32(pending) then return end -local fake_lsp_server_file = 'test/functional/fixtures/fake-lsp-server.lua' +-- Fake LSP server. +local fake_lsp_code = 'test/functional/fixtures/fake-lsp-server.lua' +local fake_lsp_logfile = 'Xtest-fake-lsp.log' + +teardown(function() + os.remove(fake_lsp_logfile) +end) local function fake_lsp_server_setup(test_name, timeout_ms) exec_lua([=[ lsp = require('vim.lsp') - local test_name, fixture_filename, timeout = ... + local test_name, fixture_filename, logfile, timeout = ... TEST_RPC_CLIENT_ID = lsp.start_client { cmd_env = { - NVIM_LOG_FILE = 'Xtest-fake-lsp-server.log' + NVIM_LOG_FILE = logfile; }; cmd = { vim.v.progpath, '-Es', '-u', 'NONE', '--headless', @@ -48,7 +55,7 @@ local function fake_lsp_server_setup(test_name, timeout_ms) vim.rpcnotify(1, "exit", ...) end; } - ]=], test_name, fake_lsp_server_file, timeout_ms or 1e3) + ]=], test_name, fake_lsp_code, fake_lsp_logfile, timeout_ms or 1e3) end local function test_rpc_server(config) @@ -114,11 +121,11 @@ describe('LSP', function() local test_name = "basic_init" exec_lua([=[ lsp = require('vim.lsp') - local test_name, fixture_filename = ... + local test_name, fixture_filename, logfile = ... function test__start_client() return lsp.start_client { cmd_env = { - NVIM_LOG_FILE = 'Xtest-fake-lsp-server.log' + NVIM_LOG_FILE = logfile; }; cmd = { vim.v.progpath, '-Es', '-u', 'NONE', '--headless', @@ -129,7 +136,7 @@ describe('LSP', function() } end TEST_CLIENT1 = test__start_client() - ]=], test_name, fake_lsp_server_file) + ]=], test_name, fake_lsp_code, fake_lsp_logfile) end) after_each(function() @@ -198,8 +205,8 @@ describe('LSP', function() end; -- If the program timed out, then code will be nil. on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; -- Note that NIL must be used here. -- on_callback(err, method, result, client_id) @@ -220,8 +227,8 @@ describe('LSP', function() client.stop() end; on_exit = function(code, signal) - eq(101, code, "exit code") -- See fake-lsp-server.lua - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 101, code, "exit code") -- See fake-lsp-server.lua + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") @@ -242,8 +249,8 @@ describe('LSP', function() client.notify('exit') end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") @@ -261,8 +268,8 @@ describe('LSP', function() client.stop() end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") @@ -301,8 +308,8 @@ describe('LSP', function() client.notify('finish') end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") @@ -344,8 +351,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -387,8 +394,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -430,8 +437,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -479,8 +486,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -528,8 +535,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -577,8 +584,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -621,8 +628,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -672,8 +679,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -715,8 +722,8 @@ describe('LSP', function() client.stop(true) end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") -- cgit From 6e13b9d26134210f0963341bd77c64a4437f37ec Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 16 Feb 2020 19:02:09 -0800 Subject: test/LSP: assert contents of log file --- test/functional/plugin/lsp_spec.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index b2d00a400a..03e516d6f6 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -1,11 +1,13 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_log = helpers.assert_log local clear = helpers.clear local buf_lines = helpers.buf_lines local dedent = helpers.dedent local exec_lua = helpers.exec_lua local eq = helpers.eq local eq_dumplog = helpers.eq_dumplog +local pesc = helpers.pesc local insert = helpers.insert local retry = helpers.retry local NIL = helpers.NIL @@ -229,6 +231,8 @@ describe('LSP', function() on_exit = function(code, signal) eq_dumplog(fake_lsp_logfile, 101, code, "exit code") -- See fake-lsp-server.lua eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + assert_log(pesc([[assert_eq failed: left == "\"shutdown\"", right == "\"test\""]]), + fake_lsp_logfile) end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") -- cgit From b353a5c05f026f46aeef0843007ba9c553533248 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 16 Feb 2020 23:30:24 -0800 Subject: test: always dump logs on failure #11886 Whenever `eq()`, `ok()`, etc. fails, include log tail in the failure message. This helps to correlate log messages with a particular test failure. --- test/functional/plugin/lsp_spec.lua | 57 ++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 29 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 03e516d6f6..ba1eb2113e 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -6,7 +6,6 @@ local buf_lines = helpers.buf_lines local dedent = helpers.dedent local exec_lua = helpers.exec_lua local eq = helpers.eq -local eq_dumplog = helpers.eq_dumplog local pesc = helpers.pesc local insert = helpers.insert local retry = helpers.retry @@ -207,8 +206,8 @@ describe('LSP', function() end; -- If the program timed out, then code will be nil. on_exit = function(code, signal) - eq_dumplog(fake_lsp_logfile, 0, code, "exit code") - eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) end; -- Note that NIL must be used here. -- on_callback(err, method, result, client_id) @@ -229,8 +228,8 @@ describe('LSP', function() client.stop() end; on_exit = function(code, signal) - eq_dumplog(fake_lsp_logfile, 101, code, "exit code") -- See fake-lsp-server.lua - eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + eq(101, code, "exit code", fake_lsp_logfile) -- See fake-lsp-server.lua + eq(0, signal, "exit signal", fake_lsp_logfile) assert_log(pesc([[assert_eq failed: left == "\"shutdown\"", right == "\"test\""]]), fake_lsp_logfile) end; @@ -253,8 +252,8 @@ describe('LSP', function() client.notify('exit') end; on_exit = function(code, signal) - eq_dumplog(fake_lsp_logfile, 0, code, "exit code") - eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") @@ -272,8 +271,8 @@ describe('LSP', function() client.stop() end; on_exit = function(code, signal) - eq_dumplog(fake_lsp_logfile, 0, code, "exit code") - eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") @@ -312,8 +311,8 @@ describe('LSP', function() client.notify('finish') end; on_exit = function(code, signal) - eq_dumplog(fake_lsp_logfile, 0, code, "exit code") - eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) end; on_callback = function(err, method, params, client_id) eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") @@ -355,8 +354,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq_dumplog(fake_lsp_logfile, 0, code, "exit code") - eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -398,8 +397,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq_dumplog(fake_lsp_logfile, 0, code, "exit code") - eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -441,8 +440,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq_dumplog(fake_lsp_logfile, 0, code, "exit code") - eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -490,8 +489,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq_dumplog(fake_lsp_logfile, 0, code, "exit code") - eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -539,8 +538,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq_dumplog(fake_lsp_logfile, 0, code, "exit code") - eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -588,8 +587,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq_dumplog(fake_lsp_logfile, 0, code, "exit code") - eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -632,8 +631,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq_dumplog(fake_lsp_logfile, 0, code, "exit code") - eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -683,8 +682,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq_dumplog(fake_lsp_logfile, 0, code, "exit code") - eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -726,8 +725,8 @@ describe('LSP', function() client.stop(true) end; on_exit = function(code, signal) - eq_dumplog(fake_lsp_logfile, 0, code, "exit code") - eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) end; on_callback = function(err, method, params, client_id) eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") -- cgit From 16262472cda88d0a4dc5c6729cfef36264569324 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Sat, 22 Feb 2020 21:20:38 +0900 Subject: lsp: add 'textDocument/documentSymbol’ callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol --- test/functional/plugin/lsp_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index ba1eb2113e..369b826adf 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -241,7 +241,7 @@ describe('LSP', function() it('should succeed with manual shutdown', function() local expected_callbacks = { - {NIL, "shutdown", {}, 1}; + {NIL, "shutdown", {}, 1, NIL}; {NIL, "test", {}, 1}; } test_rpc_server { -- cgit From a0d2bfeeb5c5b344bbddf496c3259e26f0974203 Mon Sep 17 00:00:00 2001 From: Andrey Avramenko Date: Mon, 20 Apr 2020 18:35:54 +0300 Subject: test: add get_completion_word test for text_doc... ...ument_completion_list_to_complete_items --- test/functional/plugin/lsp_spec.lua | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 369b826adf..7e9abaac2b 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -813,3 +813,35 @@ describe('LSP', function() end) end) end) + +describe('LSP', function() + describe('completion_list_to_complete_items', function() + it('should choose right completion option ', function () + local prefix = 'foo' + local completion_list = { + -- resolves into label + { label='foobar' }, + { label='foobar', textEdit={} }, + -- resolves into insertText + { label='foocar', insertText='foobar' }, + { label='foocar', insertText='foobar', textEdit={} }, + -- resolves into textEdit.newText + { label='foocar', insertText='foodar', textEdit={newText='foobar'} }, + { label='foocar', textEdit={newText='foobar'} } + } + local completion_list_items = {items=completion_list} + local expected = { + { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, + { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, + } + + eq(expected, exec_lua([[return vim.lsp.util.text_document_completion_list_to_complete_items(...)]], completion_list, prefix)) + eq(expected, exec_lua([[return vim.lsp.util.text_document_completion_list_to_complete_items(...)]], completion_list_items, prefix)) + eq({}, exec_lua([[return vim.lsp.util.text_document_completion_list_to_complete_items(...)]], {}, prefix)) + end) + end) +end) -- cgit From 49045b173e0d67a2d140cf914513dc99d2d0d51b Mon Sep 17 00:00:00 2001 From: Andrey Avramenko Date: Mon, 20 Apr 2020 20:20:14 +0300 Subject: test: add docs for get_completion_word test --- test/functional/plugin/lsp_spec.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 7e9abaac2b..b21e344acd 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -812,11 +812,12 @@ describe('LSP', function() }, buf_lines(1)) end) end) -end) -describe('LSP', function() describe('completion_list_to_complete_items', function() - it('should choose right completion option ', function () + -- Completion option precedence: + -- textEdit.newText > insertText > label + -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion + it('should choose right completion option', function () local prefix = 'foo' local completion_list = { -- resolves into label -- cgit From ef0398fe88e6cc74f33fb20519997774168d7832 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sat, 25 Apr 2020 15:46:58 +0200 Subject: LSP: Expose diagnostics grouped by bufnr (#11932) Expose `vim.lsp.buf.diagnostics_by_buf` This makes it easier to customize the diagnostics behavior. For example to defer the update they can override the `textDocument/publishDiagnostics` callback to only call `buf_diagnostics_save_positions` and then defer the other actions to a autocmd event. --- test/functional/plugin/lsp_spec.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index b21e344acd..a57443f909 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -845,4 +845,20 @@ describe('LSP', function() eq({}, exec_lua([[return vim.lsp.util.text_document_completion_list_to_complete_items(...)]], {}, prefix)) end) end) + describe('buf_diagnostics_save_positions', function() + it('stores the diagnostics in diagnostics_by_buf', function () + local diagnostics = { + { range = {}; message = "diag1" }, + { range = {}; message = "diag2" }, + } + exec_lua([[ + vim.lsp.util.buf_diagnostics_save_positions(...)]], 0, diagnostics) + eq(1, exec_lua [[ return #vim.lsp.util.diagnostics_by_buf ]]) + eq(diagnostics, exec_lua [[ + for _, diagnostics in pairs(vim.lsp.util.diagnostics_by_buf) do + return diagnostics + end + ]]) + end) + end) end) -- cgit From 50ff37308abde6c33c2800529cd58f1d7413d7b3 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Sun, 26 Apr 2020 23:56:30 +0200 Subject: LSP: Fix show_line_diagnostics #12186 Messed this up in ef0398fe88e6cc74f33fb20519997774168d7832 --- test/functional/plugin/lsp_spec.lua | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index a57443f909..e53fb5b9e2 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -861,4 +861,29 @@ describe('LSP', function() ]]) end) end) + describe('lsp.util.show_line_diagnostics', function() + it('creates floating window and returns popup bufnr and winnr if current line contains diagnostics', function() + eq(3, exec_lua [[ + local buffer = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(buffer, 0, -1, false, { + "testing"; + "123"; + }) + local diagnostics = { + { + range = { + start = { line = 0; character = 1; }; + ["end"] = { line = 0; character = 3; }; + }; + severity = vim.lsp.protocol.DiagnosticSeverity.Error; + message = "Syntax error"; + }, + } + vim.api.nvim_win_set_buf(0, buffer) + vim.lsp.util.buf_diagnostics_save_positions(vim.fn.bufnr(buffer), diagnostics) + local popup_bufnr, winnr = vim.lsp.util.show_line_diagnostics() + return popup_bufnr + ]]) + end) + end) end) -- cgit From e4a1be779b9a6bb9a47700ebf92f8dd934bb1712 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Wed, 29 Apr 2020 10:32:34 +0900 Subject: lsp/completion: Expose completion_item under completed_items.user_data. By passing through completion_item it's now possible for snippet plugins to add LSP snippet support. --- test/functional/plugin/lsp_spec.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index e53fb5b9e2..5e4f768e7f 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -832,12 +832,12 @@ describe('LSP', function() } local completion_list_items = {items=completion_list} local expected = { - { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, - { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar'}, + { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label = 'foobar' } } } } }, + { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foobar', textEdit={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar' } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar', textEdit={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar', textEdit={newText='foobar'} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', textEdit={newText='foobar'} } } } } }, } eq(expected, exec_lua([[return vim.lsp.util.text_document_completion_list_to_complete_items(...)]], completion_list, prefix)) -- cgit From f9055c585f597fe4ea8ecb990927a2826234393c Mon Sep 17 00:00:00 2001 From: Ghjuvan Lacambre Date: Wed, 29 Apr 2020 16:53:13 +0200 Subject: LSP: enable using different highlighting rules for LSP signs (#12176) This commit creates 4 new highlight groups: - LspDiagnosticsErrorSign - LspDiagnosticsWarningSign - LspDiagnosticsInformationSign - LspDiagnosticsHintSign These highlight groups are linked to their corresponding LspDiagnostics highlight groups by default. This lets users choose a different color for their sign columns and virtualtext diagnostics. --- test/functional/plugin/lsp_spec.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index e53fb5b9e2..b73da12ae8 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -754,14 +754,18 @@ describe('LSP', function() it('highlight groups', function() eq({'LspDiagnosticsError', + 'LspDiagnosticsErrorSign', 'LspDiagnosticsHint', + 'LspDiagnosticsHintSign', 'LspDiagnosticsInformation', + 'LspDiagnosticsInformationSign', 'LspDiagnosticsUnderline', 'LspDiagnosticsUnderlineError', 'LspDiagnosticsUnderlineHint', 'LspDiagnosticsUnderlineInformation', 'LspDiagnosticsUnderlineWarning', 'LspDiagnosticsWarning', + 'LspDiagnosticsWarningSign', }, exec_lua([[require'vim.lsp'; return vim.fn.getcompletion('Lsp', 'highlight')]])) end) -- cgit From 6dc8398944fd86038b07d77fcab92cd282555dee Mon Sep 17 00:00:00 2001 From: ckipp01 Date: Mon, 27 Apr 2020 10:55:06 +0200 Subject: [LSP] check for vim.NIL and add apply_text_document_edit tests --- test/functional/plugin/lsp_spec.lua | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index fdbe45c09a..53530eb513 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -817,6 +817,62 @@ describe('LSP', function() end) end) + describe('apply_text_document_edit', function() + before_each(function() + insert(dedent([[ + First line of text + Second line of text]])) + end) + it('correctly goes ahead with the edit when all is normal', function() + local text_document_edit = { + edits = { + make_edit(0, 0, 0, 0, "hi") + }, + textDocument = { + uri = "file://fake/uri"; + version = 5 + } + } + exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit, 1) + eq({ + 'hiline of text'; + 'Second line of text'; + }, buf_lines(1)) + end) + it('correctly goes ahead with the edit whe the version is nil', function() + local text_document_edit = { + edits = { + make_edit(0, 0, 0, 0, "hi") + }, + textDocument = { + uri = "file://fake/uri"; + version = vim.NIL + } + } + exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit, 1) + eq({ + 'hiline of text'; + 'Second line of text'; + }, buf_lines(1)) + end) + it('skips the edit if the version of the edit is behind the local buffer ', function() + local text_document_edit = { + edits = { + make_edit(0, 0, 0, 0, "hi") + }, + textDocument = { + uri = "file://fake/uri"; + version = 1 + } + } + exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit, 1) + eq({ + 'First line of text'; + 'Second line of text'; + }, buf_lines(1)) + end) + end) + describe('completion_list_to_complete_items', function() -- Completion option precedence: -- textEdit.newText > insertText > label -- cgit From 3eae7d52c526ae72324145a8cc096856022cc42d Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Sat, 2 May 2020 15:09:49 +0900 Subject: lsp: add lsp.util.symbols_to_items test --- test/functional/plugin/lsp_spec.lua | 251 ++++++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index fdbe45c09a..1a0063b43e 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -890,4 +890,255 @@ describe('LSP', function() ]]) end) end) + describe('lsp.util.symbols_to_items', function() + describe('convert DocumentSymbol[] to items', function() + it('DocumentSymbol has children', function() + local expected = { + { + col = 1, + filename = '', + kind = 'File', + lnum = 2, + text = '[File] TestA' + }, + { + col = 1, + filename = '', + kind = 'Module', + lnum = 4, + text = '[Module] TestB' + }, + { + col = 1, + filename = '', + kind = 'Namespace', + lnum = 6, + text = '[Namespace] TestC' + } + } + eq(expected, exec_lua [[ + local doc_syms = { + { + deprecated = false, + detail = "A", + kind = 1, + name = "TestA", + range = { + start = { + character = 0, + line = 1 + }, + ["end"] = { + character = 0, + line = 2 + } + }, + selectionRange = { + start = { + character = 0, + line = 1 + }, + ["end"] = { + character = 4, + line = 1 + } + }, + children = { + { + children = {}, + deprecated = false, + detail = "B", + kind = 2, + name = "TestB", + range = { + start = { + character = 0, + line = 3 + }, + ["end"] = { + character = 0, + line = 4 + } + }, + selectionRange = { + start = { + character = 0, + line = 3 + }, + ["end"] = { + character = 4, + line = 3 + } + } + } + } + }, + { + deprecated = false, + detail = "C", + kind = 3, + name = "TestC", + range = { + start = { + character = 0, + line = 5 + }, + ["end"] = { + character = 0, + line = 6 + } + }, + selectionRange = { + start = { + character = 0, + line = 5 + }, + ["end"] = { + character = 4, + line = 5 + } + } + } + } + return vim.lsp.util.symbols_to_items(doc_syms, nil) + ]]) + end) + it('DocumentSymbol has no children', function() + local expected = { + { + col = 1, + filename = '', + kind = 'File', + lnum = 2, + text = '[File] TestA' + }, + { + col = 1, + filename = '', + kind = 'Namespace', + lnum = 6, + text = '[Namespace] TestC' + } + } + eq(expected, exec_lua [[ + local doc_syms = { + { + deprecated = false, + detail = "A", + kind = 1, + name = "TestA", + range = { + start = { + character = 0, + line = 1 + }, + ["end"] = { + character = 0, + line = 2 + } + }, + selectionRange = { + start = { + character = 0, + line = 1 + }, + ["end"] = { + character = 4, + line = 1 + } + }, + }, + { + deprecated = false, + detail = "C", + kind = 3, + name = "TestC", + range = { + start = { + character = 0, + line = 5 + }, + ["end"] = { + character = 0, + line = 6 + } + }, + selectionRange = { + start = { + character = 0, + line = 5 + }, + ["end"] = { + character = 4, + line = 5 + } + } + } + } + return vim.lsp.util.symbols_to_items(doc_syms, nil) + ]]) + end) + end) + describe('convert SymbolInformation[] to items', function() + local expected = { + { + col = 1, + filename = 'test_a', + kind = 'File', + lnum = 2, + text = '[File] TestA' + }, + { + col = 1, + filename = 'test_b', + kind = 'Module', + lnum = 4, + text = '[Module] TestB' + } + } + eq(expected, exec_lua [[ + local sym_info = { + { + deprecated = false, + kind = 1, + name = "TestA", + location = { + range = { + start = { + character = 0, + line = 1 + }, + ["end"] = { + character = 0, + line = 2 + } + }, + uri = "file://test_a" + }, + contanerName = "TestAContainer" + }, + { + deprecated = false, + kind = 2, + name = "TestB", + location = { + range = { + start = { + character = 0, + line = 3 + }, + ["end"] = { + character = 0, + line = 4 + } + }, + uri = "file://test_b" + }, + contanerName = "TestBContainer" + } + } + return vim.lsp.util.symbols_to_items(sym_info, nil) + ]]) + end) + end) end) -- cgit From 0107a194fa6a82c3d919af2e2b606eeb74bc2352 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Sun, 3 May 2020 13:01:00 +0900 Subject: lsp: fix apply_text_document_edit test lsp.util.buf_versions must be set in advance. Use helper.insert to create an anonymous buffer, so create a named buffer for testing without using insert. --- test/functional/plugin/lsp_spec.lua | 67 +++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 21 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 53530eb513..94e85d6fe6 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -818,58 +818,83 @@ describe('LSP', function() end) describe('apply_text_document_edit', function() + local target_bufnr before_each(function() - insert(dedent([[ - First line of text - Second line of text]])) + target_bufnr = exec_lua [[ + local bufnr = vim.fn.bufadd("fake/uri") + local lines = {"1st line of text", "2nd line of text"} + vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) + return bufnr + ]] end) - it('correctly goes ahead with the edit when all is normal', function() + it('correctly goes ahead with the edit if all is normal', function() local text_document_edit = { edits = { - make_edit(0, 0, 0, 0, "hi") + make_edit(0, 0, 0, 3, "First") }, textDocument = { uri = "file://fake/uri"; version = 5 } } - exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit, 1) + exec_lua([[ + local args = {...} + local target_bufnr = args[2] + vim.lsp.util.buf_versions[target_bufnr] = 4 + vim.lsp.util.apply_text_document_edit(...) + ]], text_document_edit, target_bufnr) eq({ - 'hiline of text'; - 'Second line of text'; - }, buf_lines(1)) + 'First line of text'; + '2nd line of text'; + }, buf_lines(target_bufnr)) end) - it('correctly goes ahead with the edit whe the version is nil', function() + it('correctly goes ahead with the edit if the version is vim.NIL', function() + -- we get vim.NIL when we decode json null value. + local json = exec_lua[[ + return vim.fn.json_decode("{ \"a\": 1, \"b\": null }") + ]] + eq(json.b, exec_lua("return vim.NIL")) + local text_document_edit = { edits = { - make_edit(0, 0, 0, 0, "hi") + make_edit(0, 0, 0, 3, "First") }, textDocument = { uri = "file://fake/uri"; - version = vim.NIL + version = exec_lua("return vim.NIL") } } - exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit, 1) + exec_lua([[ + local args = {...} + local target_bufnr = args[2] + vim.lsp.util.buf_versions[target_bufnr] = vim.NIL + vim.lsp.util.apply_text_document_edit(...) + ]], text_document_edit, target_bufnr) eq({ - 'hiline of text'; - 'Second line of text'; - }, buf_lines(1)) + 'First line of text'; + '2nd line of text'; + }, buf_lines(target_bufnr)) end) it('skips the edit if the version of the edit is behind the local buffer ', function() local text_document_edit = { edits = { - make_edit(0, 0, 0, 0, "hi") + make_edit(0, 0, 0, 3, "First") }, textDocument = { uri = "file://fake/uri"; version = 1 } } - exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit, 1) + exec_lua([[ + local args = {...} + local target_bufnr = args[2] + vim.lsp.util.buf_versions[target_bufnr] = 2 + vim.lsp.util.apply_text_document_edit(...) + ]], text_document_edit, target_bufnr) eq({ - 'First line of text'; - 'Second line of text'; - }, buf_lines(1)) + '1st line of text'; + '2nd line of text'; + }, buf_lines(target_bufnr)) end) end) -- cgit From 67634da71403737bd3f0c4c037b9ccf3382903ae Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Mon, 4 May 2020 09:05:16 +0900 Subject: lsp: add a lsp.util.apply_text_edits test(pending) We don't handle non-ASCII characters well in UTF-16. So I add a non-ASCII characters test case. --- test/functional/plugin/lsp_spec.lua | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 94e85d6fe6..63188a9b09 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -770,13 +770,14 @@ describe('LSP', function() exec_lua([[require'vim.lsp'; return vim.fn.getcompletion('Lsp', 'highlight')]])) end) - describe('apply_edits', function() + describe('apply_text_edits', function() before_each(function() insert(dedent([[ First line of text Second line of text Third line of text - Fourth line of text]])) + Fourth line of text + å å ɧ 汉语 ↥ 🤦 🦄]])) end) it('applies apply simple edits', function() local edits = { @@ -790,6 +791,7 @@ describe('LSP', function() '2econd line of text'; '3ird line of text'; 'Fourth line of text'; + 'å å ɧ 汉语 ↥ 🤦 🦄'; }, buf_lines(1)) end) it('applies complex edits', function() @@ -813,6 +815,21 @@ describe('LSP', function() 'The next line of text'; 'another line of text'; 'before this!'; + 'å å ɧ 汉语 ↥ 🤦 🦄'; + }, buf_lines(1)) + end) + pending('applies non-ASCII characters edits', function() + -- FIXME: We don't handle non-ASCII characters well in UTF-16 + local edits = { + make_edit(4, 0, 4, 14, {"a a h"}); + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + eq({ + 'First line of text'; + 'Second line of text'; + 'Third line of text'; + 'Fourth line of text'; + 'a a h'; }, buf_lines(1)) end) end) -- cgit From 9a67b030d9a054648296b45b615684dee768582d Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Fri, 8 May 2020 05:23:25 +0900 Subject: lsp: Handle unknown CompletionItemKind and SymbolKind (#12257) * lsp: handle kinds not specified in protocol fix: #12200 If the client set "symbolKind.valueSet", the client must handle it properly even if it receives a value outside the specification. * test: add lsp.util.{get_completion_item_kind_name, get_symbol_kind_name} test case * lsp: make lsp.util.{get_completion_item_kind_name, get_symbol_kind_name} private --- test/functional/plugin/lsp_spec.lua | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 79f6ef9dd2..372c6ef451 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -934,12 +934,12 @@ describe('LSP', function() } local completion_list_items = {items=completion_list} local expected = { - { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label = 'foobar' } } } } }, - { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foobar', textEdit={} } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar' } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar', textEdit={} } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar', textEdit={newText='foobar'} } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = '', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', textEdit={newText='foobar'} } } } } }, + { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label = 'foobar' } } } } }, + { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foobar', textEdit={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar' } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar', textEdit={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar', textEdit={newText='foobar'} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', textEdit={newText='foobar'} } } } } }, } eq(expected, exec_lua([[return vim.lsp.util.text_document_completion_list_to_complete_items(...)]], completion_list, prefix)) @@ -1239,4 +1239,28 @@ describe('LSP', function() ]]) end) end) + + describe('lsp.util._get_completion_item_kind_name', function() + describe('returns the name specified by protocol', function() + eq("Text", exec_lua("return vim.lsp.util._get_completion_item_kind_name(1)")) + eq("TypeParameter", exec_lua("return vim.lsp.util._get_completion_item_kind_name(25)")) + end) + describe('returns the name not specified by protocol', function() + eq("Unknown", exec_lua("return vim.lsp.util._get_completion_item_kind_name(nil)")) + eq("Unknown", exec_lua("return vim.lsp.util._get_completion_item_kind_name(vim.NIL)")) + eq("Unknown", exec_lua("return vim.lsp.util._get_completion_item_kind_name(1000)")) + end) + end) + + describe('lsp.util._get_symbol_kind_name', function() + describe('returns the name specified by protocol', function() + eq("File", exec_lua("return vim.lsp.util._get_symbol_kind_name(1)")) + eq("TypeParameter", exec_lua("return vim.lsp.util._get_symbol_kind_name(26)")) + end) + describe('returns the name not specified by protocol', function() + eq("Unknown", exec_lua("return vim.lsp.util._get_symbol_kind_name(nil)")) + eq("Unknown", exec_lua("return vim.lsp.util._get_symbol_kind_name(vim.NIL)")) + eq("Unknown", exec_lua("return vim.lsp.util._get_symbol_kind_name(1000)")) + end) + end) end) -- cgit From 281e44f7bb24866d4a580d32aaeab8c8033f1fb0 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 8 May 2020 16:04:41 +0200 Subject: lsp: Make apply_text_edits non-ASCII safe (#12223) * Make apply_text_edits non-ASCII safe Use `vim.str_byteindex` to correct starting and ending positions for text edits if the line contains non-ASCII characters. Fixes #12221 * text_edit may be applied to other buffers * make sure the buffer is loaded * add comments * add test for non-ASCII edits --- test/functional/plugin/lsp_spec.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 372c6ef451..a263c6527d 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -779,7 +779,7 @@ describe('LSP', function() Fourth line of text å å ɧ 汉语 ↥ 🤦 🦄]])) end) - it('applies apply simple edits', function() + it('applies simple edits', function() local edits = { make_edit(0, 0, 0, 0, {"123"}); make_edit(1, 0, 1, 1, {"2"}); @@ -818,10 +818,9 @@ describe('LSP', function() 'å å ɧ 汉语 ↥ 🤦 🦄'; }, buf_lines(1)) end) - pending('applies non-ASCII characters edits', function() - -- FIXME: We don't handle non-ASCII characters well in UTF-16 + it('applies non-ASCII characters edits', function() local edits = { - make_edit(4, 0, 4, 14, {"a a h"}); + make_edit(4, 3, 4, 4, {"ä"}); } exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) eq({ @@ -829,7 +828,7 @@ describe('LSP', function() 'Second line of text'; 'Third line of text'; 'Fourth line of text'; - 'a a h'; + 'å ä ɧ 汉语 ↥ 🤦 🦄'; }, buf_lines(1)) end) end) -- cgit From 55b62a937c27715f7e6c299ae312c38edf08fa74 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Mon, 11 May 2020 16:56:35 +0200 Subject: LSP: Make applyEdit return a response (#12270) According to the specification workspace/applyEdit needs to respond with a `ApplyWorkspaceEditResponse` See https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit This is a subset of https://github.com/neovim/neovim/pull/11607 --- test/functional/plugin/lsp_spec.lua | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index a263c6527d..e48d50ff47 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -913,7 +913,21 @@ describe('LSP', function() }, buf_lines(target_bufnr)) end) end) - + describe('workspace_apply_edit', function() + it('workspace/applyEdit returns ApplyWorkspaceEditResponse', function() + local expected = { + applied = true; + failureReason = nil; + } + eq(expected, exec_lua [[ + local apply_edit = { + label = nil; + edit = {}; + } + return vim.lsp.callbacks['workspace/applyEdit'](nil, nil, apply_edit) + ]]) + end) + end) describe('completion_list_to_complete_items', function() -- Completion option precedence: -- textEdit.newText > insertText > label -- cgit From 02155f5c102539d5052912956606a452a78ad78c Mon Sep 17 00:00:00 2001 From: landerlo Date: Thu, 14 May 2020 04:14:52 +0100 Subject: lsp: fix bug when documentEdit version=null for unattached buffer (#12272) --- test/functional/plugin/lsp_spec.lua | 94 +++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 52 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index e48d50ff47..62dee7df90 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -835,33 +835,30 @@ describe('LSP', function() describe('apply_text_document_edit', function() local target_bufnr + local text_document_edit = function(editVersion) + return { + edits = { + make_edit(0, 0, 0, 3, "First ↥ 🤦 🦄") + }, + textDocument = { + uri = "file://fake/uri"; + version = editVersion + } + } + end before_each(function() target_bufnr = exec_lua [[ - local bufnr = vim.fn.bufadd("fake/uri") - local lines = {"1st line of text", "2nd line of text"} + local bufnr = vim.uri_to_bufnr("file://fake/uri") + local lines = {"1st line of text", "2nd line of 语text"} vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) return bufnr ]] end) it('correctly goes ahead with the edit if all is normal', function() - local text_document_edit = { - edits = { - make_edit(0, 0, 0, 3, "First") - }, - textDocument = { - uri = "file://fake/uri"; - version = 5 - } - } - exec_lua([[ - local args = {...} - local target_bufnr = args[2] - vim.lsp.util.buf_versions[target_bufnr] = 4 - vim.lsp.util.apply_text_document_edit(...) - ]], text_document_edit, target_bufnr) + exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit(5)) eq({ - 'First line of text'; - '2nd line of text'; + 'First ↥ 🤦 🦄 line of text'; + '2nd line of 语text'; }, buf_lines(target_bufnr)) end) it('correctly goes ahead with the edit if the version is vim.NIL', function() @@ -871,45 +868,38 @@ describe('LSP', function() ]] eq(json.b, exec_lua("return vim.NIL")) - local text_document_edit = { - edits = { - make_edit(0, 0, 0, 3, "First") - }, - textDocument = { - uri = "file://fake/uri"; - version = exec_lua("return vim.NIL") - } - } - exec_lua([[ - local args = {...} - local target_bufnr = args[2] - vim.lsp.util.buf_versions[target_bufnr] = vim.NIL - vim.lsp.util.apply_text_document_edit(...) - ]], text_document_edit, target_bufnr) + exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit(exec_lua("return vim.NIL"))) eq({ - 'First line of text'; - '2nd line of text'; + 'First ↥ 🤦 🦄 line of text'; + '2nd line of 语text'; }, buf_lines(target_bufnr)) end) it('skips the edit if the version of the edit is behind the local buffer ', function() - local text_document_edit = { - edits = { - make_edit(0, 0, 0, 3, "First") - }, - textDocument = { - uri = "file://fake/uri"; - version = 1 - } + local apply_edit_mocking_current_version = function(edit, versionedBuf) + exec_lua([[ + local args = {...} + local versionedBuf = args[2] + vim.lsp.util.buf_versions[versionedBuf.bufnr] = versionedBuf.currentVersion + vim.lsp.util.apply_text_document_edit(...) + ]], edit, versionedBuf) + end + + local baseText = { + '1st line of text'; + '2nd line of 语text'; } - exec_lua([[ - local args = {...} - local target_bufnr = args[2] - vim.lsp.util.buf_versions[target_bufnr] = 2 - vim.lsp.util.apply_text_document_edit(...) - ]], text_document_edit, target_bufnr) + + eq(baseText, buf_lines(target_bufnr)) + + -- Apply an edit for an old version, should skip + apply_edit_mocking_current_version(text_document_edit(2), {currentVersion=7; bufnr=target_bufnr}) + eq(baseText, buf_lines(target_bufnr)) -- no change + + -- Sanity check that next version to current does apply change + apply_edit_mocking_current_version(text_document_edit(8), {currentVersion=7; bufnr=target_bufnr}) eq({ - '1st line of text'; - '2nd line of text'; + 'First ↥ 🤦 🦄 line of text'; + '2nd line of 语text'; }, buf_lines(target_bufnr)) end) end) -- cgit From 4fbbe1c957509393563be9492d03b9e95bb08e6a Mon Sep 17 00:00:00 2001 From: Andreas Johansson Date: Sun, 17 May 2020 19:47:14 +0200 Subject: lsp: Handle end lines in apply_text_edits (#12314) If the LSP sends an end line that is larger than what nvim considers to be the last line, you get an Index out of bounds error when fetching the line from nvim, a change that was introduced in #12223. This change removes the strict indexing and checks the return value from nvim_buf_get_lines. --- test/functional/plugin/lsp_spec.lua | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 62dee7df90..f41a5323a8 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -831,6 +831,30 @@ describe('LSP', function() 'å ä ɧ 汉语 ↥ 🤦 🦄'; }, buf_lines(1)) end) + + describe('with LSP end line after what Vim considers to be the end line', function() + it('applies edits when the last linebreak is considered a new line', function() + local edits = { + make_edit(0, 0, 5, 0, {"All replaced"}); + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + eq({'All replaced'}, buf_lines(1)) + end) + it('applies edits when the end line is 2 larger than vim\'s', function() + local edits = { + make_edit(0, 0, 6, 0, {"All replaced"}); + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + eq({'All replaced'}, buf_lines(1)) + end) + it('applies edits with a column offset', function() + local edits = { + make_edit(0, 0, 5, 2, {"All replaced"}); + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + eq({'All replaced'}, buf_lines(1)) + end) + end) end) describe('apply_text_document_edit', function() -- cgit From 67eb3bfbc38b8d46dfb458698f1dc5f952229d4a Mon Sep 17 00:00:00 2001 From: Andreas Johansson Date: Tue, 19 May 2020 08:51:10 +0200 Subject: Add tests for jump_to_location --- test/functional/plugin/lsp_spec.lua | 61 +++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index f41a5323a8..5fe44d1bf2 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -1290,4 +1290,65 @@ describe('LSP', function() eq("Unknown", exec_lua("return vim.lsp.util._get_symbol_kind_name(1000)")) end) end) + + describe('lsp.util.jump_to_location', function() + local target_bufnr + + before_each(function() + target_bufnr = exec_lua [[ + local bufnr = vim.uri_to_bufnr("file://fake/uri") + local lines = {"1st line of text", "å å ɧ 汉语 ↥ 🤦 🦄"} + vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) + return bufnr + ]] + end) + + local location = function(start_line, start_char, end_line, end_char) + return { + uri = "file://fake/uri", + range = { + start = { line = start_line, character = start_char }, + ["end"] = { line = end_line, character = end_char }, + }, + } + end + + local jump = function(msg) + eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg)) + eq(target_bufnr, exec_lua[[return vim.fn.bufnr('%')]]) + return { + line = exec_lua[[return vim.fn.line('.')]], + col = exec_lua[[return vim.fn.col('.')]], + } + end + + it('jumps to a Location', function() + local pos = jump(location(0, 9, 0, 9)) + eq(1, pos.line) + eq(10, pos.col) + end) + + it('jumps to a LocationLink', function() + local pos = jump({ + targetUri = "file://fake/uri", + targetSelectionRange = { + start = { line = 0, character = 4 }, + ["end"] = { line = 0, character = 4 }, + }, + targetRange = { + start = { line = 1, character = 5 }, + ["end"] = { line = 1, character = 5 }, + }, + }) + eq(1, pos.line) + eq(5, pos.col) + end) + + it('jumps to the correct multibyte column', function() + local pos = jump(location(1, 2, 1, 2)) + eq(2, pos.line) + eq(4, pos.col) + eq('å', exec_lua[[return vim.fn.expand('')]]) + end) + end) end) -- cgit From 15b762761ad966f91d452fdd28c718f2fd3e45be Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Tue, 26 May 2020 21:55:45 +0900 Subject: lsp: make the command error message more detailed (#11633) * lsp.lua: make the error message more detailed * test: add lsp._cmd_part test --- test/functional/plugin/lsp_spec.lua | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index f41a5323a8..e39511d0ea 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -6,6 +6,7 @@ local buf_lines = helpers.buf_lines local dedent = helpers.dedent local exec_lua = helpers.exec_lua local eq = helpers.eq +local pcall_err = helpers.pcall_err local pesc = helpers.pesc local insert = helpers.insert local retry = helpers.retry @@ -705,7 +706,6 @@ describe('LSP', function() end; } end) - end) describe("parsing tests", function() @@ -733,7 +733,23 @@ describe('LSP', function() end; } end) + end) + describe('lsp._cmd_parts test', function() + local function _cmd_parts(input) + return exec_lua([[ + lsp = require('vim.lsp') + return lsp._cmd_parts(...) + ]], input) + end + it('should valid cmd argument', function() + eq(true, pcall(_cmd_parts, {"nvim"})) + eq(true, pcall(_cmd_parts, {"nvim", "--head"})) + end) + it('should invalid cmd argument', function() + eq('Error executing lua: .../shared.lua: cmd: expected list, got nvim', pcall_err(_cmd_parts, "nvim")) + eq('Error executing lua: .../shared.lua: cmd argument: expected string, got number', pcall_err(_cmd_parts, {"nvim", 1})) + end) end) end) -- cgit From 5a9226c800d3075821203952da7c38626180680d Mon Sep 17 00:00:00 2001 From: Viktor Kojouharov Date: Thu, 28 May 2020 14:31:56 +0200 Subject: lua: simple snippet support in the completion items (#12118) Old behavior is: foo(${placeholder: bar, ...) with lots of random garbage you'd never want inserted. New behavior is: foo(bar, baz) (which maybe is good, maybe is bad [depends on user], but definitely better than it was). ----- * Implement rudimentary snippet parsing Add support for parsing and discarding snippet tokens from the completion items. Fixes #11982 * Enable snippet support * Functional tests for snippet parsing Add simplified real-world snippet text examples to the completion items test * Add a test for nested snippet tokens * Remove TODO comment * Return the unmodified item if the format is plain text * Add a plain text completion item --- test/functional/plugin/lsp_spec.lua | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index e39511d0ea..f1478c782d 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -973,7 +973,14 @@ describe('LSP', function() { label='foocar', insertText='foobar', textEdit={} }, -- resolves into textEdit.newText { label='foocar', insertText='foodar', textEdit={newText='foobar'} }, - { label='foocar', textEdit={newText='foobar'} } + { label='foocar', textEdit={newText='foobar'} }, + -- real-world snippet text + { label='foocar', insertText='foodar', textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} }, + { label='foocar', insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', textEdit={} }, + -- nested snippet tokens + { label='foocar', insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', textEdit={} }, + -- plain text + { label='foocar', insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} }, } local completion_list_items = {items=completion_list} local expected = { @@ -983,6 +990,10 @@ describe('LSP', function() { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar', textEdit={} } } } } }, { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar', textEdit={newText='foobar'} } } } } }, { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', textEdit={newText='foobar'} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar(place holder, more ...holder{})', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar', textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ1, var2 *typ2) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', textEdit={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ2,typ3 tail) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', textEdit={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(${1:var1})', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} } } } } }, } eq(expected, exec_lua([[return vim.lsp.util.text_document_completion_list_to_complete_items(...)]], completion_list, prefix)) -- cgit From ac5a3f2c56775040a1b7de438cfd592c0dc26400 Mon Sep 17 00:00:00 2001 From: notomo Date: Thu, 4 Jun 2020 21:48:48 +0900 Subject: lua: fix behavior when split empty string (#12429) * lua: fix behavior when split empty string * test: lsp.util.apply_text_edits with an empty edit --- test/functional/plugin/lsp_spec.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index ea0f52c85b..479741d717 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -800,13 +800,14 @@ describe('LSP', function() make_edit(0, 0, 0, 0, {"123"}); make_edit(1, 0, 1, 1, {"2"}); make_edit(2, 0, 2, 2, {"3"}); + make_edit(3, 2, 3, 4, {""}); } exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) eq({ '123First line of text'; '2econd line of text'; '3ird line of text'; - 'Fourth line of text'; + 'Foth line of text'; 'å å ɧ 汉语 ↥ 🤦 🦄'; }, buf_lines(1)) end) -- cgit From b7f3f11049c6847a2b0c4bbd89e8339036e00da6 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 4 Jun 2020 20:23:03 +0200 Subject: lsp: compute height of floating preview correctly for wrapped lines (#12380) * take wrapping into account when computing float height * factor out size calculation * add test * accept and pass through opts.wrap_at in floating_preview * make padding configurable * slightly refactor fancy_floating_markdown to make use of make_position * padding using string.format * move trim and pad to separate function * nit Co-authored-by: Hirokazu Hata * remove mention of backward compat * make lint happy Co-authored-by: Hirokazu Hata --- test/functional/plugin/lsp_spec.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 479741d717..ae436360c3 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -1379,4 +1379,14 @@ describe('LSP', function() eq('å', exec_lua[[return vim.fn.expand('')]]) end) end) + + describe('lsp.util._make_floating_popup_size', function() + exec_lua [[ contents = + {"text tαxt txtα tex", + "text tααt tααt text", + "text tαxt tαxt"} + ]] + eq({19,3}, exec_lua[[ return {vim.lsp.util._make_floating_popup_size(contents)} ]]) + eq({15,5}, exec_lua[[ return {vim.lsp.util._make_floating_popup_size(contents,{width = 15, wrap_at = 14})} ]]) + end) end) -- cgit From 6d1404faf0e4515e409044aff195656d5e6830a1 Mon Sep 17 00:00:00 2001 From: David Lukes Date: Fri, 12 Jun 2020 02:34:34 +0200 Subject: test: Fix ignored LSP tests (#12470) * Fix ignored LSP tests * Restructure _make_floating_popup_size tests Co-authored-by: Christian Clason Co-authored-by: Christian Clason --- test/functional/plugin/lsp_spec.lua | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index ae436360c3..5a8a9106a5 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -1232,7 +1232,7 @@ describe('LSP', function() ]]) end) end) - describe('convert SymbolInformation[] to items', function() + it('convert SymbolInformation[] to items', function() local expected = { { col = 1, @@ -1296,11 +1296,11 @@ describe('LSP', function() end) describe('lsp.util._get_completion_item_kind_name', function() - describe('returns the name specified by protocol', function() + it('returns the name specified by protocol', function() eq("Text", exec_lua("return vim.lsp.util._get_completion_item_kind_name(1)")) eq("TypeParameter", exec_lua("return vim.lsp.util._get_completion_item_kind_name(25)")) end) - describe('returns the name not specified by protocol', function() + it('returns the name not specified by protocol', function() eq("Unknown", exec_lua("return vim.lsp.util._get_completion_item_kind_name(nil)")) eq("Unknown", exec_lua("return vim.lsp.util._get_completion_item_kind_name(vim.NIL)")) eq("Unknown", exec_lua("return vim.lsp.util._get_completion_item_kind_name(1000)")) @@ -1308,11 +1308,11 @@ describe('LSP', function() end) describe('lsp.util._get_symbol_kind_name', function() - describe('returns the name specified by protocol', function() + it('returns the name specified by protocol', function() eq("File", exec_lua("return vim.lsp.util._get_symbol_kind_name(1)")) eq("TypeParameter", exec_lua("return vim.lsp.util._get_symbol_kind_name(26)")) end) - describe('returns the name not specified by protocol', function() + it('returns the name not specified by protocol', function() eq("Unknown", exec_lua("return vim.lsp.util._get_symbol_kind_name(nil)")) eq("Unknown", exec_lua("return vim.lsp.util._get_symbol_kind_name(vim.NIL)")) eq("Unknown", exec_lua("return vim.lsp.util._get_symbol_kind_name(1000)")) @@ -1381,12 +1381,20 @@ describe('LSP', function() end) describe('lsp.util._make_floating_popup_size', function() - exec_lua [[ contents = - {"text tαxt txtα tex", - "text tααt tααt text", - "text tαxt tαxt"} - ]] - eq({19,3}, exec_lua[[ return {vim.lsp.util._make_floating_popup_size(contents)} ]]) - eq({15,5}, exec_lua[[ return {vim.lsp.util._make_floating_popup_size(contents,{width = 15, wrap_at = 14})} ]]) + before_each(function() + exec_lua [[ contents = + {"text tαxt txtα tex", + "text tααt tααt text", + "text tαxt tαxt"} + ]] + end) + + it('calculates size correctly', function() + eq({19,3}, exec_lua[[ return {vim.lsp.util._make_floating_popup_size(contents)} ]]) + end) + + it('calculates size correctly with wrapping', function() + eq({15,5}, exec_lua[[ return {vim.lsp.util._make_floating_popup_size(contents,{width = 15, wrap_at = 14})} ]]) + end) end) end) -- cgit From 44fe8828f06a22bc9aa3617a6fd8aae447a838de Mon Sep 17 00:00:00 2001 From: Andreas Johansson Date: Sun, 14 Jun 2020 21:23:16 +0200 Subject: lsp: Fix text edits with the same start position (#12434) According to the LSP spec[1], multiple edits can have the same starting position, and if that is the case, they should be applied in the order as they come in the array. The implementation uses a reverse sort to not interfere with non applied edits, but failed to take into account the spec. [1] https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textedit --- test/functional/plugin/lsp_spec.lua | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 5a8a9106a5..1ab81a0ef8 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -811,10 +811,33 @@ describe('LSP', function() 'å å ɧ 汉语 ↥ 🤦 🦄'; }, buf_lines(1)) end) + it('handles edits with the same start position, applying changes in the order in the array', function() + local edits = { + make_edit(0, 6, 0, 10, {""}); + make_edit(0, 6, 0, 6, {"REPLACE"}); + make_edit(1, 0, 1, 3, {""}); + make_edit(1, 0, 1, 0, {"123"}); + make_edit(2, 16, 2, 18, {""}); + make_edit(2, 16, 2, 16, {"XYZ"}); + make_edit(3, 7, 3, 11, {"this"}); + make_edit(3, 7, 3, 11, {"will"}); + make_edit(3, 7, 3, 11, {"not "}); + make_edit(3, 7, 3, 11, {"show"}); + make_edit(3, 7, 3, 11, {"(but this will)"}); + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + eq({ + 'First REPLACE of text'; + '123ond line of text'; + 'Third line of teXYZ'; + 'Fourth (but this will) of text'; + 'å å ɧ 汉语 ↥ 🤦 🦄'; + }, buf_lines(1)) + end) it('applies complex edits', function() local edits = { - make_edit(0, 0, 0, 0, {"", "12"}); make_edit(0, 0, 0, 0, {"3", "foo"}); + make_edit(0, 0, 0, 0, {"", "12"}); make_edit(0, 1, 0, 1, {"bar", "123"}); make_edit(0, #"First ", 0, #"First line of text", {"guy"}); make_edit(1, 0, 1, #'Second', {"baz"}); -- cgit From 70d4b31b834395fe36f4886e43b63fd4dc62ded1 Mon Sep 17 00:00:00 2001 From: francisco souza <108725+fsouza@users.noreply.github.com> Date: Thu, 18 Jun 2020 08:04:49 -0400 Subject: lsp: Add new highlight groups used in show_line_diagnostics (#12473) * lsp: support custom hl groups in show_line_diagnostics Closes #12472. * runtime: add docs for the new lsp highlight groups Co-authored-by: francisco souza --- test/functional/plugin/lsp_spec.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 1ab81a0ef8..c74ed6bf3b 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -770,10 +770,13 @@ describe('LSP', function() it('highlight groups', function() eq({'LspDiagnosticsError', + 'LspDiagnosticsErrorFloating', 'LspDiagnosticsErrorSign', 'LspDiagnosticsHint', + 'LspDiagnosticsHintFloating', 'LspDiagnosticsHintSign', 'LspDiagnosticsInformation', + 'LspDiagnosticsInformationFloating', 'LspDiagnosticsInformationSign', 'LspDiagnosticsUnderline', 'LspDiagnosticsUnderlineError', @@ -781,6 +784,7 @@ describe('LSP', function() 'LspDiagnosticsUnderlineInformation', 'LspDiagnosticsUnderlineWarning', 'LspDiagnosticsWarning', + 'LspDiagnosticsWarningFloating', 'LspDiagnosticsWarningSign', }, exec_lua([[require'vim.lsp'; return vim.fn.getcompletion('Lsp', 'highlight')]])) -- cgit From ebee9ebe2be2bd584efefc4064a8b3a07e0505e0 Mon Sep 17 00:00:00 2001 From: David Lukes Date: Tue, 5 May 2020 17:23:45 +0200 Subject: lsp: Add sync variant of LSP formatting Also, factor out a `vim.lsp.util.get_effective_tabstop()` helper and add tests for it. --- test/functional/plugin/lsp_spec.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index c74ed6bf3b..4b12ea2557 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -1424,4 +1424,19 @@ describe('LSP', function() eq({15,5}, exec_lua[[ return {vim.lsp.util._make_floating_popup_size(contents,{width = 15, wrap_at = 14})} ]]) end) end) + + describe('lsp.util.get_effective_tabstop', function() + local function test_tabstop(tabsize, softtabstop) + exec_lua(string.format([[ + vim.api.nvim_buf_set_option(0, 'softtabstop', %d) + vim.api.nvim_buf_set_option(0, 'tabstop', 2) + vim.api.nvim_buf_set_option(0, 'shiftwidth', 3) + ]], softtabstop)) + eq(tabsize, exec_lua('return vim.lsp.util.get_effective_tabstop()')) + end + + it('with softtabstop = 1', function() test_tabstop(1, 1) end) + it('with softtabstop = 0', function() test_tabstop(2, 0) end) + it('with softtabstop = -1', function() test_tabstop(3, -1) end) + end) end) -- cgit From 554b21261ec0ad1eaac7afc21d9a1ba7fb7cabf1 Mon Sep 17 00:00:00 2001 From: Mathias Fußenegger Date: Tue, 30 Jun 2020 17:48:04 +0200 Subject: lsp: Use nvim_buf_get_lines in locations_to_items and add more tests (#12357) * LSP: Add tests & use nvim_buf_get_lines in locations_to_items This is to add support for cases where the server returns a URI in the locations that does not have a file scheme but needs to be loaded via a BufReadCmd event. * LSP: Don't iterate through all lines in locations_to_items * fixup! LSP: Don't iterate through all lines in locations_to_items * fixup! fixup! LSP: Don't iterate through all lines in locations_to_items * fixup! fixup! fixup! LSP: Don't iterate through all lines in locations_to_items --- test/functional/plugin/lsp_spec.lua | 58 +++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 4b12ea2557..1b022f50df 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -1070,6 +1070,64 @@ describe('LSP', function() ]]) end) end) + describe('lsp.util.locations_to_items', function() + it('Convert Location[] to items', function() + local expected = { + { + filename = 'fake/uri', + lnum = 1, + col = 3, + text = 'testing' + }, + } + local actual = exec_lua [[ + local bufnr = vim.uri_to_bufnr("file://fake/uri") + local lines = {"testing", "123"} + vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) + local locations = { + { + uri = 'file://fake/uri', + range = { + start = { line = 0, character = 2 }, + ['end'] = { line = 0, character = 3 }, + } + }, + } + return vim.lsp.util.locations_to_items(locations) + ]] + eq(expected, actual) + end) + it('Convert LocationLink[] to items', function() + local expected = { + { + filename = 'fake/uri', + lnum = 1, + col = 3, + text = 'testing' + }, + } + local actual = exec_lua [[ + local bufnr = vim.uri_to_bufnr("file://fake/uri") + local lines = {"testing", "123"} + vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) + local locations = { + { + targetUri = vim.uri_from_bufnr(bufnr), + targetRange = { + start = { line = 0, character = 2 }, + ['end'] = { line = 0, character = 3 }, + }, + targetSelectionRange = { + start = { line = 0, character = 2 }, + ['end'] = { line = 0, character = 3 }, + } + }, + } + return vim.lsp.util.locations_to_items(locations) + ]] + eq(expected, actual) + end) + end) describe('lsp.util.symbols_to_items', function() describe('convert DocumentSymbol[] to items', function() it('DocumentSymbol has children', function() -- cgit From 08efa7037e05ce229150d17db11b1b1c2419631f Mon Sep 17 00:00:00 2001 From: cbarrete <62146989+cbarrete@users.noreply.github.com> Date: Sat, 18 Jul 2020 21:10:09 +0200 Subject: lsp: Add support for call hierarchies (#12556) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * LSP: Add support for call hierarchies * LSP: Add support for call hierarchies * LSP: Add support for call hierarchies * LSP: Jump to call location Jump to the call site instead of jumping to the definition of the caller/callee. * LSP: add tests for the call hierarchy callbacks * Fix linting error Co-authored-by: Cédric Barreteau <> --- test/functional/plugin/lsp_spec.lua | 143 ++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 1b022f50df..aaa28390ea 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -1497,4 +1497,147 @@ describe('LSP', function() it('with softtabstop = 0', function() test_tabstop(2, 0) end) it('with softtabstop = -1', function() test_tabstop(3, -1) end) end) + + describe('vim.lsp.buf.outgoing_calls', function() + it('does nothing for an empty response', function() + local qflist_count = exec_lua([=[ + require'vim.lsp.callbacks'['callHierarchy/outgoingCalls']() + return #vim.fn.getqflist() + ]=]) + eq(0, qflist_count) + end) + + it('opens the quickfix list with the right caller', function() + local qflist = exec_lua([=[ + local rust_analyzer_response = { { + fromRanges = { { + ['end'] = { + character = 7, + line = 3 + }, + start = { + character = 4, + line = 3 + } + } }, + to = { + detail = "fn foo()", + kind = 12, + name = "foo", + range = { + ['end'] = { + character = 11, + line = 0 + }, + start = { + character = 0, + line = 0 + } + }, + selectionRange = { + ['end'] = { + character = 6, + line = 0 + }, + start = { + character = 3, + line = 0 + } + }, + uri = "file:///src/main.rs" + } + } } + local callback = require'vim.lsp.callbacks'['callHierarchy/outgoingCalls'] + callback(nil, nil, rust_analyzer_response) + return vim.fn.getqflist() + ]=]) + + local expected = { { + bufnr = 2, + col = 5, + lnum = 4, + module = "", + nr = 0, + pattern = "", + text = "foo", + type = "", + valid = 1, + vcol = 0 + } } + + eq(expected, qflist) + end) + end) + + describe('vim.lsp.buf.incoming_calls', function() + it('does nothing for an empty response', function() + local qflist_count = exec_lua([=[ + require'vim.lsp.callbacks'['callHierarchy/incomingCalls']() + return #vim.fn.getqflist() + ]=]) + eq(0, qflist_count) + end) + + it('opens the quickfix list with the right callee', function() + local qflist = exec_lua([=[ + local rust_analyzer_response = { { + from = { + detail = "fn main()", + kind = 12, + name = "main", + range = { + ['end'] = { + character = 1, + line = 4 + }, + start = { + character = 0, + line = 2 + } + }, + selectionRange = { + ['end'] = { + character = 7, + line = 2 + }, + start = { + character = 3, + line = 2 + } + }, + uri = "file:///src/main.rs" + }, + fromRanges = { { + ['end'] = { + character = 7, + line = 3 + }, + start = { + character = 4, + line = 3 + } + } } + } } + + local callback = require'vim.lsp.callbacks'['callHierarchy/incomingCalls'] + callback(nil, nil, rust_analyzer_response) + return vim.fn.getqflist() + ]=]) + + local expected = { { + bufnr = 2, + col = 5, + lnum = 4, + module = "", + nr = 0, + pattern = "", + text = "main", + type = "", + valid = 1, + vcol = 0 + } } + + eq(expected, qflist) + end) + end) end) -- cgit From 82bfdbfe5c4eebd98ef59b52045ffd198e7ff389 Mon Sep 17 00:00:00 2001 From: Andreas Johansson Date: Thu, 30 Jul 2020 19:37:19 +0200 Subject: Revert "lsp: Fix text edits with the same start position (#12434)" (#12564) This reverts commit 44fe8828f06a22bc9aa3617a6fd8aae447a838de. --- test/functional/plugin/lsp_spec.lua | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) (limited to 'test/functional/plugin/lsp_spec.lua') diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index aaa28390ea..1002011999 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -815,33 +815,10 @@ describe('LSP', function() 'å å ɧ 汉语 ↥ 🤦 🦄'; }, buf_lines(1)) end) - it('handles edits with the same start position, applying changes in the order in the array', function() - local edits = { - make_edit(0, 6, 0, 10, {""}); - make_edit(0, 6, 0, 6, {"REPLACE"}); - make_edit(1, 0, 1, 3, {""}); - make_edit(1, 0, 1, 0, {"123"}); - make_edit(2, 16, 2, 18, {""}); - make_edit(2, 16, 2, 16, {"XYZ"}); - make_edit(3, 7, 3, 11, {"this"}); - make_edit(3, 7, 3, 11, {"will"}); - make_edit(3, 7, 3, 11, {"not "}); - make_edit(3, 7, 3, 11, {"show"}); - make_edit(3, 7, 3, 11, {"(but this will)"}); - } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) - eq({ - 'First REPLACE of text'; - '123ond line of text'; - 'Third line of teXYZ'; - 'Fourth (but this will) of text'; - 'å å ɧ 汉语 ↥ 🤦 🦄'; - }, buf_lines(1)) - end) it('applies complex edits', function() local edits = { - make_edit(0, 0, 0, 0, {"3", "foo"}); make_edit(0, 0, 0, 0, {"", "12"}); + make_edit(0, 0, 0, 0, {"3", "foo"}); make_edit(0, 1, 0, 1, {"bar", "123"}); make_edit(0, #"First ", 0, #"First line of text", {"guy"}); make_edit(1, 0, 1, #'Second', {"baz"}); -- cgit