aboutsummaryrefslogtreecommitdiff
path: root/test/functional/plugin/lsp_spec.lua
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/plugin/lsp_spec.lua')
-rw-r--r--test/functional/plugin/lsp_spec.lua613
1 files changed, 465 insertions, 148 deletions
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 508a9f2aed..572573a3a6 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 matches = helpers.matches
local pcall_err = helpers.pcall_err
local pesc = helpers.pesc
local insert = helpers.insert
@@ -14,6 +15,7 @@ local retry = helpers.retry
local NIL = helpers.NIL
local read_file = require('test.helpers').read_file
local write_file = require('test.helpers').write_file
+local isCI = helpers.isCI
-- Use these to get access to a coroutine so that I can run async tests and use
-- yield.
@@ -216,7 +218,7 @@ describe('LSP', function()
it('should run correctly', function()
local expected_handlers = {
- {NIL, "test", {}, 1};
+ {NIL, {}, {method="test", client_id=1}};
}
test_rpc_server {
test_name = "basic_init";
@@ -241,7 +243,7 @@ describe('LSP', function()
it('should fail', function()
local expected_handlers = {
- {NIL, "test", {}, 1};
+ {NIL, {}, {method="test", client_id=1}};
}
test_rpc_server {
test_name = "basic_init";
@@ -262,15 +264,15 @@ describe('LSP', function()
end)
it('should succeed with manual shutdown', function()
- if 'openbsd' == helpers.uname() then
- pending('hangs the build on openbsd #14028, re-enable with freeze timeout #14204')
+ if isCI() then
+ pending('hangs the build on CI #14028, re-enable with freeze timeout #14204')
return
elseif helpers.skip_fragile(pending) then
return
end
local expected_handlers = {
- {NIL, "shutdown", {}, 1, NIL};
- {NIL, "test", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="test", client_id=1}};
}
test_rpc_server {
test_name = "basic_init";
@@ -292,12 +294,12 @@ describe('LSP', function()
it('client should return settings via workspace/configuration handler', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
- {NIL, "workspace/configuration", { items = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, { items = {
{ section = "testSetting1" };
{ section = "testSetting2" };
- }}, 1};
- {NIL, "start", {}, 1};
+ }}, { method="workspace/configuration", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
}
local client
test_rpc_server {
@@ -309,9 +311,9 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_handler = function(err, method, params, client_id)
- eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
- if method == 'start' then
+ on_handler = function(err, result, ctx)
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'start' then
exec_lua([=[
local client = vim.lsp.get_client_by_id(TEST_RPC_CLIENT_ID)
client.config.settings = {
@@ -319,13 +321,13 @@ describe('LSP', function()
testSetting2 = false;
}]=])
end
- if method == 'workspace/configuration' then
- local result = exec_lua([=[
+ if ctx.method == 'workspace/configuration' then
+ local server_result = exec_lua([=[
local method, params = ...
- return require'vim.lsp.handlers'['workspace/configuration'](err, method, params, TEST_RPC_CLIENT_ID)]=], method, params)
- client.notify('workspace/configuration', result)
+ return require'vim.lsp.handlers'['workspace/configuration'](err, params, {method=method, client_id=TEST_RPC_CLIENT_ID})]=], ctx.method, result)
+ client.notify('workspace/configuration', server_result)
end
- if method == 'shutdown' then
+ if ctx.method == 'shutdown' then
client.stop()
end
end;
@@ -335,19 +337,19 @@ describe('LSP', function()
clear_notrace()
fake_lsp_server_setup('workspace/configuration no settings')
eq({ NIL, NIL, }, exec_lua [[
- local params = {
+ local result = {
items = {
{section = 'foo'},
{section = 'bar'},
}
}
- return vim.lsp.handlers['workspace/configuration'](nil, nil, params, TEST_RPC_CLIENT_ID)
+ return vim.lsp.handlers['workspace/configuration'](nil, result, {client_id=TEST_RPC_CLIENT_ID})
]])
end)
it('should verify capabilities sent', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
}
test_rpc_server {
test_name = "basic_check_capabilities";
@@ -371,7 +373,7 @@ describe('LSP', function()
it('client.supports_methods() should validate capabilities', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
}
test_rpc_server {
test_name = "capabilities_for_client_supports_method";
@@ -405,7 +407,7 @@ describe('LSP', function()
it('should call unsupported_method when trying to call an unsupported method', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
}
test_rpc_server {
test_name = "capabilities_for_client_supports_method";
@@ -413,7 +415,8 @@ describe('LSP', function()
exec_lua([=[
BUFFER = vim.api.nvim_get_current_buf()
lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
- vim.lsp.handlers['textDocument/typeDefinition'] = function(err, method)
+ vim.lsp.handlers['textDocument/typeDefinition'] = function(err, result, ctx)
+ local method = ctx.method
vim.lsp._last_lsp_handler = { err = err; method = method }
end
vim.lsp._unsupported_method = function(method)
@@ -446,7 +449,7 @@ describe('LSP', function()
it('shouldn\'t call unsupported_method when no client and trying to call an unsupported method', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
}
test_rpc_server {
test_name = "capabilities_for_client_supports_method";
@@ -479,8 +482,8 @@ describe('LSP', function()
it('should not send didOpen if the buffer closes before init', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
- {NIL, "finish", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="finish", client_id=1}};
}
local client
test_rpc_server {
@@ -511,9 +514,9 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_handler = function(err, method, params, client_id)
- eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
- if method == 'finish' then
+ on_handler = function(err, result, ctx)
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'finish' then
client.stop()
end
end;
@@ -522,9 +525,9 @@ describe('LSP', function()
it('should check the body sent attaching before init', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
- {NIL, "finish", {}, 1};
- {NIL, "start", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="finish", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
}
local client
test_rpc_server {
@@ -554,12 +557,12 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_handler = function(err, method, params, client_id)
- if method == 'start' then
+ on_handler = function(err, result, ctx)
+ if ctx.method == 'start' then
client.notify('finish')
end
- eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
- if method == 'finish' then
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'finish' then
client.stop()
end
end;
@@ -568,9 +571,9 @@ describe('LSP', function()
it('should check the body sent attaching after init', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
- {NIL, "finish", {}, 1};
- {NIL, "start", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="finish", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
}
local client
test_rpc_server {
@@ -597,12 +600,12 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_handler = function(err, method, params, client_id)
- if method == 'start' then
+ on_handler = function(err, result, ctx)
+ if ctx.method == 'start' then
client.notify('finish')
end
- eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
- if method == 'finish' then
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'finish' then
client.stop()
end
end;
@@ -611,9 +614,9 @@ describe('LSP', function()
it('should check the body and didChange full', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
- {NIL, "finish", {}, 1};
- {NIL, "start", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="finish", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
}
local client
test_rpc_server {
@@ -640,8 +643,8 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_handler = function(err, method, params, client_id)
- if method == 'start' then
+ on_handler = function(err, result, ctx)
+ if ctx.method == 'start' then
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
"boop";
@@ -649,8 +652,8 @@ describe('LSP', function()
]]
client.notify('finish')
end
- eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
- if method == 'finish' then
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'finish' then
client.stop()
end
end;
@@ -659,9 +662,9 @@ describe('LSP', function()
it('should check the body and didChange full with noeol', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
- {NIL, "finish", {}, 1};
- {NIL, "start", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="finish", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
}
local client
test_rpc_server {
@@ -689,8 +692,8 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_handler = function(err, method, params, client_id)
- if method == 'start' then
+ on_handler = function(err, result, ctx)
+ if ctx.method == 'start' then
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
"boop";
@@ -698,8 +701,8 @@ describe('LSP', function()
]]
client.notify('finish')
end
- eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
- if method == 'finish' then
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'finish' then
client.stop()
end
end;
@@ -708,9 +711,9 @@ describe('LSP', function()
it('should check the body and didChange incremental', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
- {NIL, "finish", {}, 1};
- {NIL, "start", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="finish", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
}
local client
test_rpc_server {
@@ -738,8 +741,8 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_handler = function(err, method, params, client_id)
- if method == 'start' then
+ on_handler = function(err, result, ctx)
+ if ctx.method == 'start' then
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
"123boop";
@@ -747,8 +750,8 @@ describe('LSP', function()
]]
client.notify('finish')
end
- eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
- if method == 'finish' then
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'finish' then
client.stop()
end
end;
@@ -758,9 +761,9 @@ describe('LSP', function()
-- 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 editing', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
- {NIL, "finish", {}, 1};
- {NIL, "start", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="finish", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
}
local client
test_rpc_server {
@@ -787,13 +790,13 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_handler = function(err, method, params, client_id)
- if method == 'start' then
+ on_handler = function(err, result, ctx)
+ if ctx.method == 'start' then
helpers.command("normal! 1Go")
client.notify('finish')
end
- eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
- if method == 'finish' then
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'finish' then
client.stop()
end
end;
@@ -802,9 +805,9 @@ describe('LSP', function()
it('should check the body and didChange full with 2 changes', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
- {NIL, "finish", {}, 1};
- {NIL, "start", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="finish", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
}
local client
test_rpc_server {
@@ -831,8 +834,8 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_handler = function(err, method, params, client_id)
- if method == 'start' then
+ on_handler = function(err, result, ctx)
+ if ctx.method == 'start' then
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
"321";
@@ -843,8 +846,8 @@ describe('LSP', function()
]]
client.notify('finish')
end
- eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
- if method == 'finish' then
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'finish' then
client.stop()
end
end;
@@ -853,9 +856,9 @@ describe('LSP', function()
it('should check the body and didChange full lifecycle', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
- {NIL, "finish", {}, 1};
- {NIL, "start", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="finish", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
}
local client
test_rpc_server {
@@ -882,8 +885,8 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_handler = function(err, method, params, client_id)
- if method == 'start' then
+ on_handler = function(err, result,ctx)
+ if ctx.method == 'start' then
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
"321";
@@ -895,8 +898,8 @@ describe('LSP', function()
]]
client.notify('finish')
end
- eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
- if method == 'finish' then
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'finish' then
client.stop()
end
end;
@@ -907,9 +910,9 @@ describe('LSP', function()
describe("parsing tests", function()
it('should handle invalid content-length correctly', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
- {NIL, "finish", {}, 1};
- {NIL, "start", {}, 1};
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="finish", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
}
local client
test_rpc_server {
@@ -924,22 +927,22 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_handler = function(err, method, params, client_id)
- eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
+ on_handler = function(err, result, ctx)
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
end;
}
end)
it('should not trim vim.NIL from the end of a list', function()
local expected_handlers = {
- {NIL, "shutdown", {}, 1};
- {NIL, "finish", {}, 1};
- {NIL, "workspace/executeCommand", {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="finish", client_id=1}};
+ {NIL,{
arguments = { "EXTRACT_METHOD", {metadata = {}}, 3, 0, 6123, NIL },
command = "refactor.perform",
title = "EXTRACT_METHOD"
- }, 1};
- {NIL, "start", {}, 1};
+ }, {method="workspace/executeCommand", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
}
local client
test_rpc_server {
@@ -963,9 +966,9 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_handler = function(err, method, params, client_id)
- eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
- if method == 'finish' then
+ on_handler = function(err, result, ctx)
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'finish' then
client.stop()
end
end;
@@ -1014,31 +1017,6 @@ describe('LSP', function()
}
end
- it('highlight groups', function()
- eq({
- 'LspDiagnosticsDefaultError',
- 'LspDiagnosticsDefaultHint',
- 'LspDiagnosticsDefaultInformation',
- 'LspDiagnosticsDefaultWarning',
- 'LspDiagnosticsFloatingError',
- 'LspDiagnosticsFloatingHint',
- 'LspDiagnosticsFloatingInformation',
- 'LspDiagnosticsFloatingWarning',
- 'LspDiagnosticsSignError',
- 'LspDiagnosticsSignHint',
- 'LspDiagnosticsSignInformation',
- 'LspDiagnosticsSignWarning',
- 'LspDiagnosticsUnderlineError',
- 'LspDiagnosticsUnderlineHint',
- 'LspDiagnosticsUnderlineInformation',
- 'LspDiagnosticsUnderlineWarning',
- 'LspDiagnosticsVirtualTextError',
- 'LspDiagnosticsVirtualTextHint',
- 'LspDiagnosticsVirtualTextInformation',
- 'LspDiagnosticsVirtualTextWarning',
- }, exec_lua([[require'vim.lsp'; return vim.fn.getcompletion('Lsp', 'highlight')]]))
- end)
-
describe('apply_text_edits', function()
before_each(function()
insert(dedent([[
@@ -1088,6 +1066,30 @@ describe('LSP', function()
'å å ɧ 汉语 ↥ 🤦 🦄';
}, buf_lines(1))
end)
+ it('applies complex edits (reversed range)', 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 line of text", 0, #"First ", {"guy"});
+ make_edit(1, #'Second', 1, 0, {"baz"});
+ make_edit(2, #"Third", 2, #'Th', {"e next"});
+ make_edit(3, #"Fourth", 3, #'', {"another line of text", "before this"});
+ make_edit(3, #"Fourth line of text", 3, #'Fourth', {"!"});
+ }
+ 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)
it('applies non-ASCII characters edits', function()
local edits = {
make_edit(4, 3, 4, 4, {"ä"});
@@ -1116,6 +1118,86 @@ describe('LSP', function()
}, buf_lines(1))
end)
+ describe('cursor position', function()
+ it('don\'t fix the cursor if the range contains the cursor', function()
+ funcs.nvim_win_set_cursor(0, { 2, 6 })
+ local edits = {
+ make_edit(1, 0, 1, 19, 'Second line of text')
+ }
+ 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';
+ 'å å ɧ 汉语 ↥ 🤦 🦄';
+ }, buf_lines(1))
+ eq({ 2, 6 }, funcs.nvim_win_get_cursor(0))
+ end)
+
+ it('fix the cursor to the valid column if the content was removed', function()
+ funcs.nvim_win_set_cursor(0, { 2, 6 })
+ local edits = {
+ make_edit(1, 0, 1, 19, '')
+ }
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ eq({
+ 'First line of text';
+ '';
+ 'Third line of text';
+ 'Fourth line of text';
+ 'å å ɧ 汉语 ↥ 🤦 🦄';
+ }, buf_lines(1))
+ eq({ 2, 0 }, funcs.nvim_win_get_cursor(0))
+ end)
+
+ it('fix the cursor row', function()
+ funcs.nvim_win_set_cursor(0, { 3, 0 })
+ local edits = {
+ make_edit(1, 0, 2, 0, '')
+ }
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ eq({
+ 'First line of text';
+ 'Third line of text';
+ 'Fourth line of text';
+ 'å å ɧ 汉语 ↥ 🤦 🦄';
+ }, buf_lines(1))
+ eq({ 2, 0 }, funcs.nvim_win_get_cursor(0))
+ end)
+
+ it('fix the cursor col', function()
+ funcs.nvim_win_set_cursor(0, { 2, 11 })
+ local edits = {
+ make_edit(1, 7, 1, 11, '')
+ }
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ eq({
+ 'First line of text';
+ 'Second of text';
+ 'Third line of text';
+ 'Fourth line of text';
+ 'å å ɧ 汉语 ↥ 🤦 🦄';
+ }, buf_lines(1))
+ eq({ 2, 7 }, funcs.nvim_win_get_cursor(0))
+ end)
+
+ it('fix the cursor row and col', function()
+ funcs.nvim_win_set_cursor(0, { 2, 12 })
+ local edits = {
+ make_edit(0, 11, 1, 12, '')
+ }
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ eq({
+ 'First line of text';
+ 'Third line of text';
+ 'Fourth line of text';
+ 'å å ɧ 汉语 ↥ 🤦 🦄';
+ }, buf_lines(1))
+ eq({ 1, 11 }, funcs.nvim_win_get_cursor(0))
+ end)
+ 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 = {
@@ -1149,14 +1231,14 @@ describe('LSP', function()
make_edit(0, 0, 0, 3, "First ↥ 🤦 🦄")
},
textDocument = {
- uri = "file://fake/uri";
+ uri = "file:///fake/uri";
version = editVersion
}
}
end
before_each(function()
target_bufnr = exec_lua [[
- local bufnr = vim.uri_to_bufnr("file://fake/uri")
+ 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
@@ -1223,7 +1305,7 @@ describe('LSP', function()
label = nil;
edit = {};
}
- return vim.lsp.handlers['workspace/applyEdit'](nil, nil, apply_edit)
+ return vim.lsp.handlers['workspace/applyEdit'](nil, apply_edit)
]])
end)
end)
@@ -1236,7 +1318,7 @@ describe('LSP', function()
make_edit(row, 0, row, 1000, new_line)
},
textDocument = {
- uri = "file://fake/uri";
+ uri = "file:///fake/uri";
version = editVersion
}
}
@@ -1254,7 +1336,7 @@ describe('LSP', function()
before_each(function()
local ret = exec_lua [[
- local bufnr = vim.uri_to_bufnr("file://fake/uri")
+ local bufnr = vim.uri_to_bufnr("file:///fake/uri")
local lines = {
"Original Line #1",
"Original Line #2"
@@ -1441,8 +1523,10 @@ describe('LSP', function()
{ label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', insertTextFormat=2, textEdit={} },
-- nested snippet tokens
{ label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', insertTextFormat=2, textEdit={} },
+ -- braced tabstop
+ { label='foocar', sortText="j", insertText='foodar()${0}', insertTextFormat=2, textEdit={} },
-- plain text
- { label='foocar', sortText="j", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} },
+ { label='foocar', sortText="k", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} },
}
local completion_list_items = {items=completion_list}
local expected = {
@@ -1454,8 +1538,9 @@ 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', sortText="f", 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', sortText="g", insertText='foodar', insertTextFormat=2, 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', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', insertTextFormat=2, 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', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', insertTextFormat=2, 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', sortText="j", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} } } } } },
+ { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ2 tail) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', insertTextFormat=2, textEdit={} } } } } },
+ { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar()', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="j", insertText='foodar()${0}', insertTextFormat=2, 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', sortText="k", 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))
@@ -1534,19 +1619,19 @@ describe('LSP', function()
it('Convert Location[] to items', function()
local expected = {
{
- filename = 'fake/uri',
+ filename = '/fake/uri',
lnum = 1,
col = 3,
text = 'testing'
},
}
local actual = exec_lua [[
- local bufnr = vim.uri_to_bufnr("file://fake/uri")
+ 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',
+ uri = 'file:///fake/uri',
range = {
start = { line = 0, character = 2 },
['end'] = { line = 0, character = 3 },
@@ -1560,14 +1645,14 @@ describe('LSP', function()
it('Convert LocationLink[] to items', function()
local expected = {
{
- filename = 'fake/uri',
+ filename = '/fake/uri',
lnum = 1,
col = 3,
text = 'testing'
},
}
local actual = exec_lua [[
- local bufnr = vim.uri_to_bufnr("file://fake/uri")
+ 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 = {
@@ -1781,14 +1866,14 @@ describe('LSP', function()
local expected = {
{
col = 1,
- filename = 'test_a',
+ filename = '/test_a',
kind = 'File',
lnum = 2,
text = '[File] TestA'
},
{
col = 1,
- filename = 'test_b',
+ filename = '/test_b',
kind = 'Module',
lnum = 4,
text = '[Module] TestB'
@@ -1811,7 +1896,7 @@ describe('LSP', function()
line = 2
}
},
- uri = "file://test_a"
+ uri = "file:///test_a"
},
contanerName = "TestAContainer"
},
@@ -1830,7 +1915,7 @@ describe('LSP', function()
line = 4
}
},
- uri = "file://test_b"
+ uri = "file:///test_b"
},
contanerName = "TestBContainer"
}
@@ -1869,7 +1954,7 @@ describe('LSP', function()
before_each(function()
target_bufnr = exec_lua [[
- local bufnr = vim.uri_to_bufnr("file://fake/uri")
+ 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
@@ -1878,7 +1963,7 @@ describe('LSP', function()
local location = function(start_line, start_char, end_line, end_char)
return {
- uri = "file://fake/uri",
+ uri = "file:///fake/uri",
range = {
start = { line = start_line, character = start_char },
["end"] = { line = end_line, character = end_char },
@@ -1903,7 +1988,7 @@ describe('LSP', function()
it('jumps to a LocationLink', function()
local pos = jump({
- targetUri = "file://fake/uri",
+ targetUri = "file:///fake/uri",
targetSelectionRange = {
start = { line = 0, character = 4 },
["end"] = { line = 0, character = 4 },
@@ -1937,6 +2022,83 @@ describe('LSP', function()
end)
end)
+ describe('lsp.util.make_floating_popup_options', function()
+ before_each(function()
+ exec_lua [[
+ local bufnr = vim.uri_to_bufnr("file:///fake/uri")
+ local winheight = vim.fn.winheight(0)
+ for i = 1, winheight do
+ vim.api.nvim_buf_set_lines(bufnr, 0, 0, false, {''})
+ end
+ vim.api.nvim_win_set_buf(0, bufnr)
+ vim.api.nvim_win_set_cursor(0, {winheight, 0})
+ ]]
+ end)
+
+ local function popup_row(opts)
+ return exec_lua([[
+ return vim.lsp.util.make_floating_popup_options(...).row
+ ]], 2, 2, opts)
+ end
+
+ local err_pattern = "^Error executing lua: %.%.%./util%.lua:0: invalid floating preview border: .*%. :help vim%.api%.nvim_open_win%(%)$"
+
+ it('calculates default border height correctly', function()
+ eq(0, popup_row())
+ end)
+
+ it('calculates string border height correctly', function()
+ eq(0, popup_row({border = 'none'}))
+ eq(-2, popup_row({border = 'single'}))
+ eq(-2, popup_row({border = 'double'}))
+ eq(-2, popup_row({border = 'rounded'}))
+ eq(-2, popup_row({border = 'solid'}))
+ eq(-1, popup_row({border = 'shadow'}))
+ end)
+
+ it('error on invalid string border', function()
+ matches(err_pattern, pcall_err(popup_row, {border = ''}))
+ matches(err_pattern, pcall_err(popup_row, {border = 'invalid'}))
+ end)
+
+ it('error on invalid array border length', function()
+ matches(err_pattern, pcall_err(popup_row, {border = {}}))
+ matches(err_pattern, pcall_err(popup_row, {border = {'', '', ''}}))
+ matches(err_pattern, pcall_err(popup_row, {border = {'', '', '', '', ''}}))
+ end)
+
+ it('error on invalid array border member type', function()
+ matches(err_pattern, pcall_err(popup_row, {border = {0}}))
+ end)
+
+ it('calculates 8-array border height correctly', function()
+ eq(0, popup_row({border = {'', '', '', '', '', '', '', ''}}))
+ eq(-2, popup_row({border = {'', '~', '', '~', '', '~', '', '~'}}))
+ eq(-1, popup_row({border = {'', '', '', '~', '', '~', '', ''}}))
+ eq(0, popup_row({border = {'', '', '', {'~', 'NormalFloat'}, '', '', '', {'~', 'NormalFloat'}}}))
+ eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}, '', '', '', {'~', 'NormalFloat'}, '', ''}}))
+ end)
+
+ it('calculates 4-array border height correctly', function()
+ eq(0, popup_row({border = {'', '', '', ''}}))
+ eq(-2, popup_row({border = {'', '~', '', '~'}}))
+ eq(0, popup_row({border = {'', '', '', {'~', 'NormalFloat'}}}))
+ eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}, '', ''}}))
+ end)
+
+ it('calculates 2-array border height correctly', function()
+ eq(0, popup_row({border = {'', ''}}))
+ eq(-2, popup_row({border = {'', '~'}}))
+ eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}}}))
+ end)
+
+ it('calculates 1-array border height correctly', function()
+ eq(0, popup_row({border = {''}}))
+ eq(-2, popup_row({border = {'~'}}))
+ eq(-2, popup_row({border = {{'~', 'NormalFloat'}}}))
+ end)
+ end)
+
describe('lsp.util._make_floating_popup_size', function()
before_each(function()
exec_lua [[ contents =
@@ -1955,6 +2117,12 @@ describe('LSP', function()
end)
end)
+ describe('lsp.util.trim.trim_empty_lines', function()
+ it('properly trims empty lines', function()
+ eq({{"foo", "bar"}}, exec_lua[[ return vim.lsp.util.trim_empty_lines({{ "foo", "bar" }, nil}) ]])
+ end)
+ end)
+
describe('lsp.util.get_effective_tabstop', function()
local function test_tabstop(tabsize, softtabstop)
exec_lua(string.format([[
@@ -1973,7 +2141,7 @@ describe('LSP', function()
describe('vim.lsp.buf.outgoing_calls', function()
it('does nothing for an empty response', function()
local qflist_count = exec_lua([=[
- require'vim.lsp.handlers'['callHierarchy/outgoingCalls']()
+ require'vim.lsp.handlers'['callHierarchy/outgoingCalls'](nil, nil, {}, nil)
return #vim.fn.getqflist()
]=])
eq(0, qflist_count)
@@ -2020,14 +2188,16 @@ describe('LSP', function()
}
} }
local handler = require'vim.lsp.handlers'['callHierarchy/outgoingCalls']
- handler(nil, nil, rust_analyzer_response)
+ handler(nil, rust_analyzer_response, {})
return vim.fn.getqflist()
]=])
local expected = { {
bufnr = 2,
col = 5,
+ end_col = 0,
lnum = 4,
+ end_lnum = 0,
module = "",
nr = 0,
pattern = "",
@@ -2044,7 +2214,7 @@ describe('LSP', function()
describe('vim.lsp.buf.incoming_calls', function()
it('does nothing for an empty response', function()
local qflist_count = exec_lua([=[
- require'vim.lsp.handlers'['callHierarchy/incomingCalls']()
+ require'vim.lsp.handlers'['callHierarchy/incomingCalls'](nil, nil, {})
return #vim.fn.getqflist()
]=])
eq(0, qflist_count)
@@ -2092,14 +2262,16 @@ describe('LSP', function()
} }
local handler = require'vim.lsp.handlers'['callHierarchy/incomingCalls']
- handler(nil, nil, rust_analyzer_response)
+ handler(nil, rust_analyzer_response, {})
return vim.fn.getqflist()
]=])
local expected = { {
bufnr = 2,
col = 5,
+ end_col = 0,
lnum = 4,
+ end_lnum = 0,
module = "",
nr = 0,
pattern = "",
@@ -2112,4 +2284,149 @@ describe('LSP', function()
eq(expected, qflist)
end)
end)
+
+ describe('vim.lsp.buf.rename', function()
+ for _, test in ipairs({
+ {
+ it = "does not attempt to rename on nil response",
+ name = "prepare_rename_nil",
+ expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ },
+ },
+ {
+ it = "handles prepareRename placeholder response",
+ name = "prepare_rename_placeholder",
+ expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, NIL, {method="textDocument/rename", client_id=1, bufnr=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ },
+ expected_text = "placeholder", -- see fake lsp response
+ },
+ {
+ it = "handles range response",
+ name = "prepare_rename_range",
+ expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, NIL, {method="textDocument/rename", client_id=1, bufnr=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ },
+ expected_text = "line", -- see test case and fake lsp response
+ },
+ {
+ it = "handles error",
+ name = "prepare_rename_error",
+ expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, NIL, {method="textDocument/rename", client_id=1, bufnr=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ },
+ expected_text = "two", -- see test case
+ },
+ }) do
+ it(test.it, function()
+ local client
+ test_rpc_server {
+ test_name = test.name;
+ on_init = function(_client)
+ client = _client
+ eq(true, client.resolved_capabilities().rename)
+ end;
+ on_setup = function()
+ exec_lua([=[
+ local bufnr = vim.api.nvim_get_current_buf()
+ lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
+ vim.lsp._stubs = {}
+ vim.fn.input = function(prompt, text)
+ vim.lsp._stubs.input_prompt = prompt
+ vim.lsp._stubs.input_text = text
+ return 'renameto' -- expect this value in fake lsp
+ end
+ vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {'', 'this is line two'})
+ vim.fn.cursor(2, 13) -- the space between "line" and "two"
+ ]=])
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
+ end;
+ on_handler = function(err, result, ctx)
+ -- Don't compare & assert params, they're not relevant for the testcase
+ -- This allows us to be lazy and avoid declaring them
+ ctx.params = nil
+
+ eq(table.remove(test.expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'start' then
+ exec_lua("vim.lsp.buf.rename()")
+ end
+ if ctx.method == 'shutdown' then
+ if test.expected_text then
+ eq("New Name: ", exec_lua("return vim.lsp._stubs.input_prompt"))
+ eq(test.expected_text, exec_lua("return vim.lsp._stubs.input_text"))
+ end
+ client.stop()
+ end
+ end;
+ }
+ end)
+ end
+ end)
+
+ describe('vim.lsp.buf.code_action', function()
+ it('Calls client side command if available', function()
+ local client
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ test_rpc_server {
+ test_name = 'code_action_with_resolve',
+ on_init = function(client_)
+ client = client_
+ end,
+ on_setup = function()
+ end,
+ on_exit = function(code, signal)
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
+ end,
+ on_handler = function(err, result, ctx)
+ eq(table.remove(expected_handlers), {err, result, ctx})
+ if ctx.method == 'start' then
+ exec_lua([[
+ vim.lsp.commands['dummy1'] = function(cmd)
+ vim.lsp.commands['dummy2'] = function()
+ end
+ end
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
+ vim.fn.inputlist = function()
+ return 1
+ end
+ vim.lsp.buf.code_action()
+ ]])
+ elseif ctx.method == 'shutdown' then
+ eq('function', exec_lua[[return type(vim.lsp.commands['dummy2'])]])
+ client.stop()
+ end
+ end
+ }
+ end)
+ end)
+ describe('vim.lsp.commands', function()
+ it('Accepts only string keys', function()
+ matches(
+ '.*The key for commands in `vim.lsp.commands` must be a string',
+ pcall_err(exec_lua, 'vim.lsp.commands[1] = function() end')
+ )
+ end)
+ it('Accepts only function values', function()
+ matches(
+ '.*Command added to `vim.lsp.commands` must be a function',
+ pcall_err(exec_lua, 'vim.lsp.commands.dummy = 10')
+ )
+ end)
+ end)
end)