2019-11-13 12:55:26 -08:00
local helpers = require ( 'test.functional.helpers' )( after_each )
2022-12-08 08:54:19 -06:00
local lsp_helpers = require ( 'test.functional.plugin.lsp.helpers' )
2019-11-13 12:55:26 -08:00
2020-02-16 19:02:09 -08:00
local assert_log = helpers.assert_log
2020-01-08 09:46:25 -08:00
local buf_lines = helpers.buf_lines
2022-12-08 08:54:19 -06:00
local clear = helpers.clear
2021-10-29 07:45:01 -05:00
local command = helpers.command
2020-01-08 09:20:53 -08:00
local dedent = helpers.dedent
2019-11-13 12:55:26 -08:00
local exec_lua = helpers.exec_lua
local eq = helpers.eq
2021-10-29 07:45:01 -05:00
local eval = helpers.eval
2021-08-27 19:19:17 +08:00
local matches = helpers.matches
2020-05-26 21:55:45 +09:00
local pcall_err = helpers.pcall_err
2020-02-16 19:02:09 -08:00
local pesc = helpers.pesc
2020-01-08 09:20:53 -08:00
local insert = helpers.insert
2021-05-19 15:35:30 +02:00
local funcs = helpers.funcs
2019-12-31 07:51:54 -08:00
local retry = helpers.retry
2022-12-08 08:54:19 -06:00
local stop = helpers.stop
2019-11-13 12:55:26 -08:00
local NIL = helpers.NIL
2021-03-15 22:34:22 +01:00
local read_file = require ( 'test.helpers' ). read_file
local write_file = require ( 'test.helpers' ). write_file
2022-11-22 01:13:30 +01:00
local is_ci = helpers.is_ci
2022-05-09 12:00:27 -06:00
local meths = helpers.meths
2022-11-22 01:13:30 +01:00
local is_os = helpers.is_os
2022-11-13 14:52:19 +01:00
local skip = helpers.skip
2023-04-04 21:59:06 +02:00
local mkdir = helpers.mkdir
2019-11-13 12:55:26 -08:00
2022-12-08 08:54:19 -06:00
local clear_notrace = lsp_helpers.clear_notrace
local create_server_definition = lsp_helpers.create_server_definition
local fake_lsp_code = lsp_helpers.fake_lsp_code
local fake_lsp_logfile = lsp_helpers.fake_lsp_logfile
local test_rpc_server = lsp_helpers.test_rpc_server
2019-11-13 12:55:26 -08:00
2023-03-09 15:12:56 +01:00
local function get_buf_option ( name , bufnr )
bufnr = bufnr or "BUFFER"
return exec_lua ( string.format ( "return vim.api.nvim_buf_get_option(%s, '%s')" , bufnr , name ))
end
2020-02-08 18:08:02 -08:00
-- TODO(justinmk): hangs on Windows https://github.com/neovim/neovim/pull/11837
2022-11-22 01:13:30 +01:00
if skip ( is_os ( 'win' )) then return end
2019-11-13 12:55:26 -08:00
2020-02-08 18:08:02 -08:00
teardown ( function ()
os.remove ( fake_lsp_logfile )
end )
2019-11-13 12:55:26 -08:00
2019-12-31 07:51:54 -08:00
describe ( 'LSP' , function ()
2021-09-26 14:21:40 -07:00
before_each ( function ()
clear_notrace ()
2019-11-13 12:55:26 -08:00
2021-09-26 14:21:40 -07:00
-- 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')
2023-01-31 13:08:46 +01:00
local test_name, fake_lsp_code, fake_lsp_logfile = ...
2021-09-26 14:21:40 -07:00
function test__start_client()
return lsp.start_client {
cmd_env = {
2023-01-31 13:08:46 +01:00
NVIM_LOG_FILE = fake_lsp_logfile;
2023-03-10 17:30:40 +01:00
NVIM_APPNAME = "nvim_lsp_test";
2021-09-26 14:21:40 -07:00
};
cmd = {
2023-01-31 13:08:46 +01:00
vim.v.progpath, '-l', fake_lsp_code, test_name;
2021-09-26 14:21:40 -07:00
};
2021-11-21 11:39:30 -05:00
workspace_folders = {{
uri = 'file://' .. vim.loop.cwd(),
name = 'test_folder',
}};
2021-09-26 14:21:40 -07:00
}
end
TEST_CLIENT1 = test__start_client()
]=] , test_name , fake_lsp_code , fake_lsp_logfile )
end )
2019-11-13 12:55:26 -08:00
2021-09-26 14:21:40 -07:00
after_each ( function ()
2022-07-17 19:13:33 +02:00
exec_lua ( "vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })" )
2021-09-26 14:21:40 -07:00
-- exec_lua("lsp.stop_all_clients(true)")
end )
describe ( 'server_name specified' , function ()
2019-12-31 07:51:54 -08:00
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()' ))
2019-11-13 12:55:26 -08:00
end )
end )
end )
2022-05-22 14:06:21 +02:00
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 ()
2022-11-14 10:01:35 +00:00
eq ( '.../lsp.lua:0: cmd: expected list, got nvim' ,
2022-05-22 14:06:21 +02:00
pcall_err ( _cmd_parts , 'nvim' ))
2022-11-14 10:01:35 +00:00
eq ( '.../lsp.lua:0: cmd argument: expected string, got number' ,
2022-05-22 14:06:21 +02:00
pcall_err ( _cmd_parts , { 'nvim' , 1 }))
end )
end )
end )
describe ( 'LSP' , function ()
2019-11-13 12:55:26 -08:00
describe ( 'basic_init test' , function ()
2021-01-27 15:32:07 +01:00
after_each ( function ()
stop ()
2022-05-22 14:06:21 +02:00
exec_lua ( "lsp.stop_client(lsp.get_active_clients(), true)" )
2022-07-17 19:13:33 +02:00
exec_lua ( "vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })" )
2021-01-27 15:32:07 +01:00
end )
2019-11-13 12:55:26 -08:00
it ( 'should run correctly' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "test" , client_id = 1 }};
2019-11-13 12:55:26 -08:00
}
test_rpc_server {
test_name = "basic_init" ;
2021-02-23 00:02:51 +01:00
on_init = function ( client , _ )
2019-11-13 12:55:26 -08:00
-- 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 )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2019-11-13 12:55:26 -08:00
end ;
-- Note that NIL must be used here.
2021-06-22 10:35:36 +02:00
-- on_handler(err, method, result, client_id)
on_handler = function (...)
eq ( table.remove ( expected_handlers ), {...})
2019-11-13 12:55:26 -08:00
end ;
}
end )
it ( 'should fail' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "test" , client_id = 1 }};
2019-11-13 12:55:26 -08:00
}
test_rpc_server {
test_name = "basic_init" ;
on_init = function ( client )
client.notify ( 'test' )
client.stop ()
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 101 , code , "exit code" ) -- See fake-lsp-server.lua
eq ( 0 , signal , "exit signal" )
2020-02-16 19:02:09 -08:00
assert_log ( pesc ( [[assert_eq failed: left == "\"shutdown\"", right == "\"test\""]] ),
fake_lsp_logfile )
2019-11-13 12:55:26 -08:00
end ;
2021-06-22 10:35:36 +02:00
on_handler = function (...)
2021-06-28 23:02:17 +02:00
eq ( table.remove ( expected_handlers ), {...}, "expected handler" )
2019-11-13 12:55:26 -08:00
end ;
}
end )
2022-06-03 18:16:11 +02:00
it ( 'should send didChangeConfiguration after initialize if there are settings' , function ()
test_rpc_server ({
test_name = 'basic_init_did_change_configuration' ,
on_init = function ( client , _ )
client.stop ()
end ,
on_exit = function ( code , signal )
eq ( 0 , code , 'exit code' , fake_lsp_logfile )
eq ( 0 , signal , 'exit signal' , fake_lsp_logfile )
end ,
settings = {
dummy = 1 ,
},
})
end )
2019-11-13 12:55:26 -08:00
it ( 'should succeed with manual shutdown' , function ()
2022-11-22 01:13:30 +01:00
if is_ci () then
2021-07-23 18:33:07 +02:00
pending ( 'hangs the build on CI #14028, re-enable with freeze timeout #14204' )
2021-04-03 10:04:14 +02:00
return
2021-08-13 23:53:55 -04:00
elseif helpers.skip_fragile ( pending ) then
return
2021-04-03 10:04:14 +02:00
end
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-10-29 07:45:01 -05:00
{ NIL , {}, { method = "shutdown" , bufnr = 1 , client_id = 1 }};
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "test" , client_id = 1 }};
2019-11-13 12:55:26 -08:00
}
test_rpc_server {
test_name = "basic_init" ;
on_init = function ( client )
2022-04-30 02:22:30 -07:00
eq ( 0 , client.server_capabilities (). textDocumentSync.change )
2019-11-13 12:55:26 -08:00
client.request ( 'shutdown' )
client.notify ( 'exit' )
2021-01-04 19:04:21 +01:00
client.stop ()
2019-11-13 12:55:26 -08:00
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2019-11-13 12:55:26 -08:00
end ;
2021-06-22 10:35:36 +02:00
on_handler = function (...)
2021-06-28 23:02:17 +02:00
eq ( table.remove ( expected_handlers ), {...}, "expected handler" )
2019-11-13 12:55:26 -08:00
end ;
}
end )
2021-12-21 10:53:34 -08:00
it ( 'should detach buffer in response to nvim_buf_detach' , function ()
local expected_handlers = {
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "finish" , client_id = 1 }};
}
local client
test_rpc_server {
test_name = "basic_finish" ;
on_setup = function ()
exec_lua [[
BUFFER = vim.api.nvim_create_buf(false, true)
]]
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
client.notify ( 'finish' )
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2021-12-21 10:53:34 -08:00
end ;
on_handler = function ( err , result , ctx )
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == 'finish' then
exec_lua ( "return lsp.buf_detach_client(BUFFER, TEST_RPC_CLIENT_ID)" )
eq ( false , exec_lua ( "return lsp.buf_is_attached(BUFFER, TEST_RPC_CLIENT_ID)" ))
client.stop ()
end
end ;
}
end )
2022-05-09 12:00:27 -06:00
it ( 'should fire autocommands on attach and detach' , function ()
local client
test_rpc_server {
test_name = "basic_init" ;
on_setup = function ()
exec_lua [[
BUFFER = vim.api.nvim_create_buf(false, true)
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
vim.g.lsp_attached = client.name
end,
})
vim.api.nvim_create_autocmd('LspDetach', {
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
vim.g.lsp_detached = client.name
end,
})
]]
end ;
on_init = function ( _client )
client = _client
eq ( true , exec_lua ( "return lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)" ))
client.notify ( 'finish' )
end ;
on_handler = function ( _ , _ , ctx )
if ctx.method == 'finish' then
eq ( 'basic_init' , meths.get_var ( 'lsp_attached' ))
exec_lua ( "return lsp.buf_detach_client(BUFFER, TEST_RPC_CLIENT_ID)" )
eq ( 'basic_init' , meths.get_var ( 'lsp_detached' ))
client.stop ()
end
end ;
}
end )
2023-03-09 15:12:56 +01:00
it ( 'should set default options on attach' , function ()
local client
test_rpc_server {
test_name = "set_defaults_all_capabilities" ;
2023-04-11 18:37:27 +02:00
on_init = function ( _client )
client = _client
2023-03-09 15:12:56 +01:00
exec_lua [[
BUFFER = vim.api.nvim_create_buf(false, true)
2023-04-11 18:37:27 +02:00
lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
2023-03-09 15:12:56 +01:00
]]
end ;
on_handler = function ( _ , _ , ctx )
if ctx.method == 'test' then
eq ( 'v:lua.vim.lsp.tagfunc' , get_buf_option ( "tagfunc" ))
eq ( 'v:lua.vim.lsp.omnifunc' , get_buf_option ( "omnifunc" ))
eq ( 'v:lua.vim.lsp.formatexpr()' , get_buf_option ( "formatexpr" ))
client.stop ()
end
end ;
on_exit = function ( _ , _ )
eq ( '' , get_buf_option ( "tagfunc" ))
eq ( '' , get_buf_option ( "omnifunc" ))
eq ( '' , get_buf_option ( "formatexpr" ))
end ;
}
end )
it ( 'should overwrite options set by ftplugins' , function ()
local client
test_rpc_server {
test_name = "set_defaults_all_capabilities" ;
2023-04-11 18:37:27 +02:00
on_init = function ( _client )
client = _client
2023-03-09 15:12:56 +01:00
exec_lua [[
vim.api.nvim_command('filetype plugin on')
BUFFER_1 = vim.api.nvim_create_buf(false, true)
BUFFER_2 = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_option(BUFFER_1, 'filetype', 'man')
vim.api.nvim_buf_set_option(BUFFER_2, 'filetype', 'xml')
]]
2023-04-11 18:37:27 +02:00
-- Sanity check to ensure that some values are set after setting filetype.
2023-03-09 15:12:56 +01:00
eq ( 'v:lua.require \' man \' .goto_tag' , get_buf_option ( "tagfunc" , "BUFFER_1" ))
eq ( 'xmlcomplete#CompleteTags' , get_buf_option ( "omnifunc" , "BUFFER_2" ))
eq ( 'xmlformat#Format()' , get_buf_option ( "formatexpr" , "BUFFER_2" ))
2023-04-11 18:37:27 +02:00
exec_lua [[
lsp.buf_attach_client(BUFFER_1, TEST_RPC_CLIENT_ID)
lsp.buf_attach_client(BUFFER_2, TEST_RPC_CLIENT_ID)
]]
2023-03-09 15:12:56 +01:00
end ;
on_handler = function ( _ , _ , ctx )
if ctx.method == 'test' then
eq ( 'v:lua.vim.lsp.tagfunc' , get_buf_option ( "tagfunc" , "BUFFER_1" ))
eq ( 'v:lua.vim.lsp.omnifunc' , get_buf_option ( "omnifunc" , "BUFFER_2" ))
eq ( 'v:lua.vim.lsp.formatexpr()' , get_buf_option ( "formatexpr" , "BUFFER_2" ))
client.stop ()
end
end ;
on_exit = function ( _ , _ )
eq ( '' , get_buf_option ( "tagfunc" , "BUFFER_1" ))
eq ( '' , get_buf_option ( "omnifunc" , "BUFFER_2" ))
eq ( '' , get_buf_option ( "formatexpr" , "BUFFER_2" ))
end ;
}
end )
it ( 'should not overwrite user-defined options' , function ()
local client
test_rpc_server {
test_name = "set_defaults_all_capabilities" ;
2023-04-11 18:37:27 +02:00
on_init = function ( _client )
client = _client
2023-03-09 15:12:56 +01:00
exec_lua [[
BUFFER = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_option(BUFFER, 'tagfunc', 'tfu')
vim.api.nvim_buf_set_option(BUFFER, 'omnifunc', 'ofu')
vim.api.nvim_buf_set_option(BUFFER, 'formatexpr', 'fex')
2023-04-11 18:37:27 +02:00
lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
2023-03-09 15:12:56 +01:00
]]
end ;
on_handler = function ( _ , _ , ctx )
if ctx.method == 'test' then
eq ( 'tfu' , get_buf_option ( "tagfunc" ))
eq ( 'ofu' , get_buf_option ( "omnifunc" ))
eq ( 'fex' , get_buf_option ( "formatexpr" ))
client.stop ()
end
end ;
on_exit = function ( _ , _ )
eq ( 'tfu' , get_buf_option ( "tagfunc" ))
eq ( 'ofu' , get_buf_option ( "omnifunc" ))
eq ( 'fex' , get_buf_option ( "formatexpr" ))
end ;
}
end )
2022-09-08 23:09:32 +08:00
it ( 'should detach buffer on bufwipe' , function ()
2022-09-13 06:01:35 +08:00
clear ()
2022-12-04 15:39:22 +01:00
exec_lua ( create_server_definition )
2022-09-08 23:09:32 +08:00
local result = exec_lua ( [[
2022-12-04 15:39:22 +01:00
local server = _create_server()
2022-09-08 23:09:32 +08:00
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_current_buf(bufnr)
2022-12-04 15:39:22 +01:00
local client_id = vim.lsp.start({ name = 'detach-dummy', cmd = server.cmd })
2022-09-08 23:09:32 +08:00
assert(client_id, "lsp.start must return client_id")
local client = vim.lsp.get_client_by_id(client_id)
local num_attached_before = vim.tbl_count(client.attached_buffers)
vim.api.nvim_buf_delete(bufnr, { force = true })
local num_attached_after = vim.tbl_count(client.attached_buffers)
return {
bufnr = bufnr,
client_id = client_id,
num_attached_before = num_attached_before,
num_attached_after = num_attached_after,
}
]] )
eq ( true , result ~= nil , "exec_lua must return result" )
eq ( 1 , result.num_attached_before )
eq ( 0 , result.num_attached_after )
end )
2020-12-31 05:41:43 -08:00
it ( 'client should return settings via workspace/configuration handler' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , { items = {
2020-12-31 05:41:43 -08:00
{ section = "testSetting1" };
{ section = "testSetting2" };
2022-04-15 11:12:41 +02:00
{ section = "test.Setting3" };
{ section = "test.Setting4" };
2021-08-28 00:12:30 -04:00
}}, { method = "workspace/configuration" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
2020-12-31 05:41:43 -08:00
}
local client
test_rpc_server {
test_name = "check_workspace_configuration" ;
on_init = function ( _client )
client = _client
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2020-12-31 05:41:43 -08:00
end ;
2021-08-28 00:12:30 -04:00
on_handler = function ( err , result , ctx )
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == 'start' then
2020-12-31 05:41:43 -08:00
exec_lua ( [=[
local client = vim.lsp.get_client_by_id(TEST_RPC_CLIENT_ID)
client.config.settings = {
testSetting1 = true;
testSetting2 = false;
2022-04-15 11:12:41 +02:00
test = {Setting3 = 'nested' };
2020-12-31 05:41:43 -08:00
}]=] )
end
2021-08-28 00:12:30 -04:00
if ctx.method == 'workspace/configuration' then
local server_result = exec_lua ( [=[
2020-12-31 05:41:43 -08:00
local method, params = ...
2021-08-28 00:12:30 -04:00
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 )
2020-12-31 05:41:43 -08:00
end
2021-08-28 00:12:30 -04:00
if ctx.method == 'shutdown' then
2020-12-31 05:41:43 -08:00
client.stop ()
end
end ;
}
end )
2021-01-18 19:33:10 +01:00
it ( 'workspace/configuration returns NIL per section if client was started without config.settings' , function ()
2022-05-22 14:06:21 +02:00
local result = nil
test_rpc_server {
test_name = 'basic_init' ;
on_init = function ( c ) c.stop () end ,
on_setup = function ()
result = exec_lua [[
local result = {
items = {
{section = 'foo'},
{section = 'bar'},
}
}
return vim.lsp.handlers['workspace/configuration'](nil, result, {client_id=TEST_RPC_CLIENT_ID})
]]
end
}
eq ({ NIL , NIL }, result )
2021-01-18 19:33:10 +01:00
end )
2020-12-31 05:41:43 -08:00
2019-11-13 12:55:26 -08:00
it ( 'should verify capabilities sent' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
2019-11-13 12:55:26 -08:00
}
test_rpc_server {
test_name = "basic_check_capabilities" ;
on_init = function ( client )
client.stop ()
2020-10-25 00:28:15 -04:00
local full_kind = exec_lua ( "return require'vim.lsp.protocol'.TextDocumentSyncKind.Full" )
2022-04-30 02:22:30 -07:00
eq ( full_kind , client.server_capabilities (). textDocumentSync.change )
eq ({ includeText = false }, client.server_capabilities (). textDocumentSync.save )
eq ( false , client.server_capabilities (). codeLensProvider )
2020-10-25 00:28:15 -04:00
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2020-10-25 00:28:15 -04:00
end ;
2021-06-22 10:35:36 +02:00
on_handler = function (...)
2021-06-28 23:02:17 +02:00
eq ( table.remove ( expected_handlers ), {...}, "expected handler" )
2020-10-25 00:28:15 -04:00
end ;
}
end )
2022-07-17 19:13:33 +02:00
it ( 'BufWritePost sends didSave with bool textDocumentSync.save' , function ()
2022-04-30 22:13:26 +02:00
local expected_handlers = {
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
}
local client
test_rpc_server {
test_name = "text_document_sync_save_bool" ;
on_init = function ( c )
client = c
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2022-04-30 22:13:26 +02:00
end ;
on_handler = function ( err , result , ctx )
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == "start" then
exec_lua ( [=[
BUFFER = vim.api.nvim_get_current_buf()
lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
2022-07-17 19:13:33 +02:00
vim.api.nvim_exec_autocmds('BufWritePost', { buffer = BUFFER, modeline = false })
2022-04-30 22:13:26 +02:00
]=] )
else
client.stop ()
end
end ;
}
end )
2022-12-08 10:55:01 +01:00
it ( 'BufWritePre does not send notifications if server lacks willSave capabilities' , function ()
2022-12-08 19:22:40 +08:00
clear ()
2022-12-08 10:55:01 +01:00
exec_lua ( create_server_definition )
local messages = exec_lua ( [[
local server = _create_server({
capabilities = {
textDocumentSync = {
willSave = false,
willSaveWaitUntil = false,
}
},
})
local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
local buf = vim.api.nvim_get_current_buf()
vim.api.nvim_exec_autocmds('BufWritePre', { buffer = buf, modeline = false })
vim.lsp.stop_client(client_id)
return server.messages
]] )
eq ( # messages , 4 )
eq ( messages [ 1 ]. method , 'initialize' )
eq ( messages [ 2 ]. method , 'initialized' )
eq ( messages [ 3 ]. method , 'shutdown' )
eq ( messages [ 4 ]. method , 'exit' )
end )
2022-12-08 19:22:40 +08:00
2022-12-08 10:55:01 +01:00
it ( 'BufWritePre sends willSave / willSaveWaitUntil, applies textEdits' , function ()
2022-12-08 19:22:40 +08:00
clear ()
2022-12-08 10:55:01 +01:00
exec_lua ( create_server_definition )
local result = exec_lua ( [[
local server = _create_server({
capabilities = {
textDocumentSync = {
willSave = true,
willSaveWaitUntil = true,
}
},
handlers = {
['textDocument/willSaveWaitUntil'] = function()
local text_edit = {
range = {
start = { line = 0, character = 0 },
['end'] = { line = 0, character = 0 },
},
newText = 'Hello'
}
return { text_edit, }
end
},
})
local buf = vim.api.nvim_get_current_buf()
local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
vim.api.nvim_exec_autocmds('BufWritePre', { buffer = buf, modeline = false })
vim.lsp.stop_client(client_id)
return {
messages = server.messages,
lines = vim.api.nvim_buf_get_lines(buf, 0, -1, true)
}
]] )
local messages = result.messages
eq ( 'textDocument/willSave' , messages [ 3 ]. method )
eq ( 'textDocument/willSaveWaitUntil' , messages [ 4 ]. method )
eq ({ 'Hello' }, result.lines )
end )
2022-08-01 22:32:53 +02:00
it ( 'saveas sends didOpen if filename changed' , function ()
local expected_handlers = {
{ NIL , {}, { method = 'shutdown' , client_id = 1 } },
{ NIL , {}, { method = 'start' , client_id = 1 } },
}
local client
test_rpc_server ({
test_name = 'text_document_save_did_open' ,
on_init = function ( c )
client = c
end ,
on_exit = function ( code , signal )
eq ( 0 , code , 'exit code' )
eq ( 0 , signal , 'exit signal' )
end ,
on_handler = function ( err , result , ctx )
eq ( table.remove ( expected_handlers ), { err , result , ctx }, 'expected handler' )
if ctx.method == 'start' then
local tmpfile_old = helpers.tmpname ()
local tmpfile_new = helpers.tmpname ()
os.remove ( tmpfile_new )
exec_lua (
[=[
local oldname, newname = ...
BUFFER = vim.api.nvim_get_current_buf()
vim.api.nvim_buf_set_name(BUFFER, oldname)
vim.api.nvim_buf_set_lines(BUFFER, 0, -1, true, {"help me"})
lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
vim.api.nvim_buf_call(BUFFER, function() vim.cmd('saveas ' .. newname) end)
]=] ,
tmpfile_old ,
tmpfile_new
)
else
client.stop ()
end
end ,
})
end )
2022-07-17 19:13:33 +02:00
it ( 'BufWritePost sends didSave including text if server capability is set' , function ()
2022-04-30 22:13:26 +02:00
local expected_handlers = {
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
}
local client
test_rpc_server {
test_name = "text_document_sync_save_includeText" ;
on_init = function ( c )
client = c
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2022-04-30 22:13:26 +02:00
end ;
on_handler = function ( err , result , ctx )
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == "start" then
exec_lua ( [=[
BUFFER = vim.api.nvim_get_current_buf()
vim.api.nvim_buf_set_lines(BUFFER, 0, -1, true, {"help me"})
lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
2022-07-17 19:13:33 +02:00
vim.api.nvim_exec_autocmds('BufWritePost', { buffer = BUFFER, modeline = false })
2022-04-30 22:13:26 +02:00
]=] )
else
client.stop ()
end
end ;
}
end )
2020-10-25 00:28:15 -04:00
it ( 'client.supports_methods() should validate capabilities' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
2020-10-25 00:28:15 -04:00
}
test_rpc_server {
test_name = "capabilities_for_client_supports_method" ;
on_init = function ( client )
client.stop ()
2022-04-30 02:22:30 -07:00
local expected_sync_capabilities = {
change = 1 ,
openClose = true ,
save = { includeText = false },
willSave = false ,
willSaveWaitUntil = false ,
}
eq ( expected_sync_capabilities , client.server_capabilities (). textDocumentSync )
eq ( true , client.server_capabilities (). completionProvider )
eq ( true , client.server_capabilities (). hoverProvider )
eq ( false , client.server_capabilities (). definitionProvider )
eq ( false , client.server_capabilities (). renameProvider )
eq ( true , client.server_capabilities (). codeLensProvider.resolveProvider )
2020-10-25 00:28:15 -04:00
-- known methods for resolved capabilities
eq ( true , client.supports_method ( "textDocument/hover" ))
eq ( false , client.supports_method ( "textDocument/definition" ))
-- unknown methods are assumed to be supported.
eq ( true , client.supports_method ( "unknown-method" ))
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2020-10-25 00:28:15 -04:00
end ;
2021-06-22 10:35:36 +02:00
on_handler = function (...)
2021-06-28 23:02:17 +02:00
eq ( table.remove ( expected_handlers ), {...}, "expected handler" )
2020-10-25 00:28:15 -04:00
end ;
}
end )
2021-10-10 22:32:50 -07:00
it ( 'should not call unsupported_method when trying to call an unsupported method' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
2020-10-25 00:28:15 -04:00
}
test_rpc_server {
test_name = "capabilities_for_client_supports_method" ;
on_setup = function ()
exec_lua ( [=[
2020-12-03 14:00:54 +08:00
BUFFER = vim.api.nvim_get_current_buf()
lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
2021-10-10 22:32:50 -07:00
vim.lsp.handlers['textDocument/typeDefinition'] = function() end
2020-10-25 00:28:15 -04:00
]=] )
end ;
on_init = function ( client )
client.stop ()
2021-10-10 22:32:50 -07:00
exec_lua ( "vim.lsp.buf.type_definition()" )
2020-12-03 14:00:54 +08:00
exec_lua [[
vim.api.nvim_command(BUFFER.."bwipeout")
]]
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2020-12-03 14:00:54 +08:00
end ;
2021-06-22 10:35:36 +02:00
on_handler = function (...)
2021-06-28 23:02:17 +02:00
eq ( table.remove ( expected_handlers ), {...}, "expected handler" )
2020-12-03 14:00:54 +08:00
end ;
}
end )
2021-10-10 22:32:50 -07:00
it ( 'should not call unsupported_method when no client and trying to call an unsupported method' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
2020-12-03 14:00:54 +08:00
}
test_rpc_server {
test_name = "capabilities_for_client_supports_method" ;
on_setup = function ()
exec_lua ( [=[
2021-10-10 22:32:50 -07:00
vim.lsp.handlers['textDocument/typeDefinition'] = function() end
2020-12-03 14:00:54 +08:00
]=] )
end ;
on_init = function ( client )
client.stop ()
2021-10-10 22:32:50 -07:00
exec_lua ( "vim.lsp.buf.type_definition()" )
2019-11-13 12:55:26 -08:00
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2019-11-13 12:55:26 -08:00
end ;
2021-06-22 10:35:36 +02:00
on_handler = function (...)
2021-06-28 23:02:17 +02:00
eq ( table.remove ( expected_handlers ), {...}, "expected handler" )
2019-11-13 12:55:26 -08:00
end ;
}
end )
2021-10-08 11:19:33 -07:00
it ( 'should not forward RequestCancelled to callback' , function ()
local expected_handlers = {
2021-10-08 14:01:55 -07:00
{ NIL , {}, { method = "finish" , client_id = 1 }};
2021-10-08 11:19:33 -07:00
}
local client
test_rpc_server {
test_name = "check_forward_request_cancelled" ;
on_init = function ( _client )
_client.request ( "error_code_test" )
client = _client
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2021-10-08 11:19:33 -07:00
eq ( 0 , # expected_handlers , "did not call expected handler" )
end ;
2021-10-08 14:01:55 -07:00
on_handler = function ( err , _ , ctx )
eq ( table.remove ( expected_handlers ), { err , {}, ctx }, "expected handler" )
if ctx.method == 'finish' then client.stop () end
2021-10-08 11:19:33 -07:00
end ;
}
end )
it ( 'should forward ContentModified to callback' , function ()
local expected_handlers = {
2021-10-08 14:01:55 -07:00
{ NIL , {}, { method = "finish" , client_id = 1 }};
2021-10-29 07:45:01 -05:00
{{ code = - 32801 }, NIL , { method = "error_code_test" , bufnr = 1 , client_id = 1 }};
2021-10-08 11:19:33 -07:00
}
local client
test_rpc_server {
test_name = "check_forward_content_modified" ;
on_init = function ( _client )
_client.request ( "error_code_test" )
client = _client
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2021-10-08 11:19:33 -07:00
eq ( 0 , # expected_handlers , "did not call expected handler" )
end ;
2021-10-08 14:01:55 -07:00
on_handler = function ( err , _ , ctx )
eq ( table.remove ( expected_handlers ), { err , _ , ctx }, "expected handler" )
-- if ctx.method == 'error_code_test' then client.notify("finish") end
if ctx.method ~= 'finish' then client.notify ( 'finish' ) end
if ctx.method == 'finish' then client.stop () end
2021-10-08 11:19:33 -07:00
end ;
}
end )
2021-10-29 07:45:01 -05:00
it ( 'should track pending requests to the language server' , function ()
local expected_handlers = {
{ NIL , {}, { method = "finish" , client_id = 1 }};
{ NIL , {}, { method = "slow_request" , bufnr = 1 , client_id = 1 }};
}
local client
test_rpc_server {
test_name = "check_pending_request_tracked" ;
on_init = function ( _client )
client = _client
client.request ( "slow_request" )
local request = exec_lua ( [=[ return TEST_RPC_CLIENT.requests[2] ]=] )
eq ( "slow_request" , request.method )
eq ( "pending" , request.type )
client.notify ( "release" )
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2021-10-29 07:45:01 -05:00
eq ( 0 , # expected_handlers , "did not call expected handler" )
end ;
on_handler = function ( err , _ , ctx )
eq ( table.remove ( expected_handlers ), { err , {}, ctx }, "expected handler" )
if ctx.method == 'slow_request' then
local request = exec_lua ( [=[ return TEST_RPC_CLIENT.requests[2] ]=] )
eq ( NIL , request )
client.notify ( "finish" )
end
if ctx.method == 'finish' then client.stop () end
end ;
}
end )
it ( 'should track cancel requests to the language server' , function ()
local expected_handlers = {
{ NIL , {}, { method = "finish" , client_id = 1 }};
}
local client
test_rpc_server {
test_name = "check_cancel_request_tracked" ;
on_init = function ( _client )
client = _client
client.request ( "slow_request" )
client.cancel_request ( 2 )
local request = exec_lua ( [=[ return TEST_RPC_CLIENT.requests[2] ]=] )
eq ( "slow_request" , request.method )
eq ( "cancel" , request.type )
client.notify ( "release" )
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2021-10-29 07:45:01 -05:00
eq ( 0 , # expected_handlers , "did not call expected handler" )
end ;
on_handler = function ( err , _ , ctx )
eq ( table.remove ( expected_handlers ), { err , {}, ctx }, "expected handler" )
local request = exec_lua ( [=[ return TEST_RPC_CLIENT.requests[2] ]=] )
eq ( NIL , request )
if ctx.method == 'finish' then client.stop () end
end ;
}
end )
it ( 'should clear pending and cancel requests on reply' , function ()
local expected_handlers = {
{ NIL , {}, { method = "finish" , client_id = 1 }};
{ NIL , {}, { method = "slow_request" , bufnr = 1 , client_id = 1 }};
}
local client
test_rpc_server {
test_name = "check_tracked_requests_cleared" ;
on_init = function ( _client )
client = _client
client.request ( "slow_request" )
local request = exec_lua ( [=[ return TEST_RPC_CLIENT.requests[2] ]=] )
eq ( "slow_request" , request.method )
eq ( "pending" , request.type )
client.cancel_request ( 2 )
request = exec_lua ( [=[ return TEST_RPC_CLIENT.requests[2] ]=] )
eq ( "slow_request" , request.method )
eq ( "cancel" , request.type )
client.notify ( "release" )
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2021-10-29 07:45:01 -05:00
eq ( 0 , # expected_handlers , "did not call expected handler" )
end ;
on_handler = function ( err , _ , ctx )
eq ( table.remove ( expected_handlers ), { err , {}, ctx }, "expected handler" )
if ctx.method == 'slow_request' then
local request = exec_lua ( [=[ return TEST_RPC_CLIENT.requests[2] ]=] )
eq ( NIL , request )
client.notify ( "finish" )
end
if ctx.method == 'finish' then client.stop () end
end ;
}
end )
it ( 'should trigger LspRequest autocmd when requests table changes' , function ()
local expected_handlers = {
{ NIL , {}, { method = "finish" , client_id = 1 }};
{ NIL , {}, { method = "slow_request" , bufnr = 1 , client_id = 1 }};
}
local client
test_rpc_server {
test_name = "check_tracked_requests_cleared" ;
on_init = function ( _client )
command ( 'let g:requests = 0' )
command ( 'autocmd User LspRequest let g:requests+=1' )
client = _client
client.request ( "slow_request" )
eq ( 1 , eval ( 'g:requests' ))
client.cancel_request ( 2 )
eq ( 2 , eval ( 'g:requests' ))
client.notify ( "release" )
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2021-10-29 07:45:01 -05:00
eq ( 0 , # expected_handlers , "did not call expected handler" )
eq ( 3 , eval ( 'g:requests' ))
end ;
on_handler = function ( err , _ , ctx )
eq ( table.remove ( expected_handlers ), { err , {}, ctx }, "expected handler" )
if ctx.method == 'slow_request' then
client.notify ( "finish" )
end
if ctx.method == 'finish' then client.stop () end
end ;
}
end )
2019-11-13 12:55:26 -08:00
it ( 'should not send didOpen if the buffer closes before init' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "finish" , client_id = 1 }};
2019-11-13 12:55:26 -08:00
}
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" )
2022-04-30 02:22:30 -07:00
eq ( full_kind , client.server_capabilities (). textDocumentSync.change )
eq ( true , client.server_capabilities (). textDocumentSync.openClose )
2019-11-13 12:55:26 -08:00
client.notify ( 'finish' )
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2019-11-13 12:55:26 -08:00
end ;
2021-08-28 00:12:30 -04:00
on_handler = function ( err , result , ctx )
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == 'finish' then
2019-11-13 12:55:26 -08:00
client.stop ()
end
end ;
}
end )
it ( 'should check the body sent attaching before init' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "finish" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
2019-11-13 12:55:26 -08:00
}
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" )
2022-04-30 02:22:30 -07:00
eq ( full_kind , client.server_capabilities (). textDocumentSync.change )
eq ( true , client.server_capabilities (). textDocumentSync.openClose )
2019-11-13 12:55:26 -08:00
exec_lua [[
assert(not lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID), "Shouldn't attach twice")
]]
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2019-11-13 12:55:26 -08:00
end ;
2021-08-28 00:12:30 -04:00
on_handler = function ( err , result , ctx )
if ctx.method == 'start' then
2019-11-13 12:55:26 -08:00
client.notify ( 'finish' )
end
2021-08-28 00:12:30 -04:00
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == 'finish' then
2019-11-13 12:55:26 -08:00
client.stop ()
end
end ;
}
end )
it ( 'should check the body sent attaching after init' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "finish" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
2019-11-13 12:55:26 -08:00
}
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" )
2022-04-30 02:22:30 -07:00
eq ( full_kind , client.server_capabilities (). textDocumentSync.change )
eq ( true , client.server_capabilities (). textDocumentSync.openClose )
2019-11-13 12:55:26 -08:00
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2019-11-13 12:55:26 -08:00
end ;
2021-08-28 00:12:30 -04:00
on_handler = function ( err , result , ctx )
if ctx.method == 'start' then
2019-11-13 12:55:26 -08:00
client.notify ( 'finish' )
end
2021-08-28 00:12:30 -04:00
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == 'finish' then
2019-11-13 12:55:26 -08:00
client.stop ()
end
end ;
}
end )
it ( 'should check the body and didChange full' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "finish" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
2019-11-13 12:55:26 -08:00
}
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" )
2022-04-30 02:22:30 -07:00
eq ( full_kind , client.server_capabilities (). textDocumentSync.change )
eq ( true , client.server_capabilities (). textDocumentSync.openClose )
2019-11-13 12:55:26 -08:00
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2019-11-13 12:55:26 -08:00
end ;
2021-08-28 00:12:30 -04:00
on_handler = function ( err , result , ctx )
if ctx.method == 'start' then
2019-11-21 10:29:54 +01:00
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
"boop";
})
]]
client.notify ( 'finish' )
end
2021-08-28 00:12:30 -04:00
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == 'finish' then
2019-11-21 10:29:54 +01:00
client.stop ()
end
end ;
}
end )
it ( 'should check the body and didChange full with noeol' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "finish" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
2019-11-21 10:29:54 +01:00
}
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" )
2022-04-30 02:22:30 -07:00
eq ( full_kind , client.server_capabilities (). textDocumentSync.change )
eq ( true , client.server_capabilities (). textDocumentSync.openClose )
2019-11-21 10:29:54 +01:00
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2019-11-21 10:29:54 +01:00
end ;
2021-08-28 00:12:30 -04:00
on_handler = function ( err , result , ctx )
if ctx.method == 'start' then
2019-11-13 12:55:26 -08:00
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
"boop";
})
]]
client.notify ( 'finish' )
end
2021-08-28 00:12:30 -04:00
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == 'finish' then
2019-11-13 12:55:26 -08:00
client.stop ()
end
end ;
}
end )
2021-03-07 17:18:32 -08:00
it ( 'should check the body and didChange incremental' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "finish" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
2019-11-13 12:55:26 -08:00
}
local client
test_rpc_server {
test_name = "basic_check_buffer_open_and_change_incremental" ;
2022-01-11 18:10:29 +01:00
options = {
allow_incremental_sync = true ,
};
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" )
2022-04-30 02:22:30 -07:00
eq ( sync_kind , client.server_capabilities (). textDocumentSync.change )
eq ( true , client.server_capabilities (). textDocumentSync.openClose )
2022-01-11 18:10:29 +01:00
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2022-01-11 18:10:29 +01:00
end ;
on_handler = function ( err , result , ctx )
if ctx.method == 'start' then
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
"123boop";
})
]]
client.notify ( 'finish' )
end
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == 'finish' then
client.stop ()
end
end ;
}
end )
it ( 'should check the body and didChange incremental with debounce' , function ()
local expected_handlers = {
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "finish" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
}
local client
test_rpc_server {
test_name = "basic_check_buffer_open_and_change_incremental" ;
options = {
allow_incremental_sync = true ,
debounce_text_changes = 5
};
2019-11-13 12:55:26 -08:00
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" )
2022-04-30 02:22:30 -07:00
eq ( sync_kind , client.server_capabilities (). textDocumentSync.change )
eq ( true , client.server_capabilities (). textDocumentSync.openClose )
2019-11-13 12:55:26 -08:00
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2019-11-13 12:55:26 -08:00
end ;
2021-08-28 00:12:30 -04:00
on_handler = function ( err , result , ctx )
if ctx.method == 'start' then
2019-11-13 12:55:26 -08:00
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
2021-03-07 17:18:32 -08:00
"123boop";
2019-11-13 12:55:26 -08:00
})
]]
client.notify ( 'finish' )
end
2021-08-28 00:12:30 -04:00
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == 'finish' then
2019-11-13 12:55:26 -08:00
client.stop ()
end
end ;
}
end )
-- TODO(askhan) we don't support full for now, so we can disable these tests.
2020-02-08 15:35:28 -08:00
pending ( 'should check the body and didChange incremental normal mode editing' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-10-29 07:45:01 -05:00
{ NIL , {}, { method = "shutdown" , bufnr = 1 , client_id = 1 }};
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "finish" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
2019-11-13 12:55:26 -08:00
}
local client
test_rpc_server {
2020-02-08 15:35:28 -08:00
test_name = "basic_check_buffer_open_and_change_incremental_editing" ;
2019-11-13 12:55:26 -08:00
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" )
2022-04-30 02:22:30 -07:00
eq ( sync_kind , client.server_capabilities (). textDocumentSync.change )
eq ( true , client.server_capabilities (). textDocumentSync.openClose )
2019-11-13 12:55:26 -08:00
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2019-11-13 12:55:26 -08:00
end ;
2021-08-28 00:12:30 -04:00
on_handler = function ( err , result , ctx )
if ctx.method == 'start' then
2019-11-13 12:55:26 -08:00
helpers.command ( "normal! 1Go" )
client.notify ( 'finish' )
end
2021-08-28 00:12:30 -04:00
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == 'finish' then
2019-11-13 12:55:26 -08:00
client.stop ()
end
end ;
}
end )
it ( 'should check the body and didChange full with 2 changes' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "finish" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
2019-11-13 12:55:26 -08:00
}
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" )
2022-04-30 02:22:30 -07:00
eq ( sync_kind , client.server_capabilities (). textDocumentSync.change )
eq ( true , client.server_capabilities (). textDocumentSync.openClose )
2019-11-13 12:55:26 -08:00
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2019-11-13 12:55:26 -08:00
end ;
2021-08-28 00:12:30 -04:00
on_handler = function ( err , result , ctx )
if ctx.method == 'start' then
2019-11-13 12:55:26 -08:00
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
2021-08-28 00:12:30 -04:00
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == 'finish' then
2019-11-13 12:55:26 -08:00
client.stop ()
end
end ;
}
end )
it ( 'should check the body and didChange full lifecycle' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "finish" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
2019-11-13 12:55:26 -08:00
}
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" )
2022-04-30 02:22:30 -07:00
eq ( sync_kind , client.server_capabilities (). textDocumentSync.change )
eq ( true , client.server_capabilities (). textDocumentSync.openClose )
2019-11-13 12:55:26 -08:00
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2019-11-13 12:55:26 -08:00
end ;
2021-08-28 00:12:30 -04:00
on_handler = function ( err , result , ctx )
if ctx.method == 'start' then
2019-11-13 12:55:26 -08:00
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
2021-08-28 00:12:30 -04:00
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == 'finish' then
2019-11-13 12:55:26 -08:00
client.stop ()
end
end ;
}
end )
end )
describe ( "parsing tests" , function ()
it ( 'should handle invalid content-length correctly' , function ()
2021-06-22 10:35:36 +02:00
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "finish" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
2019-11-13 12:55:26 -08:00
}
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 )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2019-11-13 12:55:26 -08:00
end ;
2021-08-28 00:12:30 -04:00
on_handler = function ( err , result , ctx )
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
2019-11-13 12:55:26 -08:00
end ;
}
end )
2021-05-18 23:28:09 +01:00
it ( 'should not trim vim.NIL from the end of a list' , function ()
local expected_handlers = {
2021-08-28 00:12:30 -04:00
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "finish" , client_id = 1 }};
{ NIL ,{
2021-05-18 23:28:09 +01:00
arguments = { "EXTRACT_METHOD" , { metadata = {}}, 3 , 0 , 6123 , NIL },
command = "refactor.perform" ,
title = "EXTRACT_METHOD"
2021-08-28 00:12:30 -04:00
}, { method = "workspace/executeCommand" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
2021-05-18 23:28:09 +01:00
}
local client
test_rpc_server {
test_name = "decode_nil" ;
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
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
end ;
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2021-05-18 23:28:09 +01:00
end ;
2021-08-28 00:12:30 -04:00
on_handler = function ( err , result , ctx )
eq ( table.remove ( expected_handlers ), { err , result , ctx }, "expected handler" )
if ctx.method == 'finish' then
2021-05-18 23:28:09 +01:00
client.stop ()
end
end ;
}
end )
2020-05-26 21:55:45 +09:00
end )
2019-11-13 12:55:26 -08:00
end )
2020-01-08 09:20:53 -08:00
2020-01-08 09:46:25 -08:00
describe ( 'LSP' , function ()
2020-01-08 09:20:53 -08:00
before_each ( function ()
2021-04-03 10:04:14 +02:00
clear_notrace ()
2020-01-08 09:20:53 -08:00
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
2020-05-04 09:05:16 +09:00
describe ( 'apply_text_edits' , function ()
2020-01-08 09:46:25 -08:00
before_each ( function ()
insert ( dedent ( [[
First line of text
Second line of text
Third line of text
2020-05-04 09:05:16 +09:00
Fourth line of text
å å ɧ 汉语 ↥ 🤦 🦄]] ))
2020-01-08 09:46:25 -08:00
end )
2020-05-08 16:04:41 +02:00
it ( 'applies simple edits' , function ()
2020-01-08 09:20:53 -08:00
local edits = {
make_edit ( 0 , 0 , 0 , 0 , { "123" });
make_edit ( 1 , 0 , 1 , 1 , { "2" });
make_edit ( 2 , 0 , 2 , 2 , { "3" });
2020-06-04 21:48:48 +09:00
make_edit ( 3 , 2 , 3 , 4 , { "" });
2020-01-08 09:20:53 -08:00
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2020-01-08 09:20:53 -08:00
eq ({
'123First line of text' ;
'2econd line of text' ;
'3ird line of text' ;
2020-06-04 21:48:48 +09:00
'Foth line of text' ;
2020-05-04 09:05:16 +09:00
'å å ɧ 汉语 ↥ 🤦 🦄' ;
2020-01-08 09:20:53 -08:00
}, buf_lines ( 1 ))
end )
2020-01-08 09:46:25 -08:00
it ( 'applies complex edits' , function ()
2020-01-08 09:20:53 -08:00
local edits = {
2020-06-14 21:23:16 +02:00
make_edit ( 0 , 0 , 0 , 0 , { "" , "12" });
2020-07-30 19:37:19 +02:00
make_edit ( 0 , 0 , 0 , 0 , { "3" , "foo" });
2020-01-08 09:20:53 -08:00
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" , { "!" });
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2020-01-08 09:20:53 -08:00
eq ({
'' ;
'123' ;
'fooFbar' ;
'123irst guy' ;
'baz line of text' ;
'The next line of text' ;
'another line of text' ;
'before this!' ;
2020-05-04 09:05:16 +09:00
'å å ɧ 汉语 ↥ 🤦 🦄' ;
2020-01-08 09:20:53 -08:00
}, buf_lines ( 1 ))
end )
2021-09-19 05:19:21 +09:00
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' , { "!" });
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2021-09-19 05:19:21 +09:00
eq ({
'' ;
'123' ;
'fooFbar' ;
'123irst guy' ;
'baz line of text' ;
'The next line of text' ;
'another line of text' ;
'before this!' ;
'å å ɧ 汉语 ↥ 🤦 🦄' ;
}, buf_lines ( 1 ))
end )
2020-05-08 16:04:41 +02:00
it ( 'applies non-ASCII characters edits' , function ()
2020-05-04 09:05:16 +09:00
local edits = {
2020-05-08 16:04:41 +02:00
make_edit ( 4 , 3 , 4 , 4 , { "ä" });
2020-05-04 09:05:16 +09:00
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2020-05-04 09:05:16 +09:00
eq ({
'First line of text' ;
'Second line of text' ;
'Third line of text' ;
'Fourth line of text' ;
2020-05-08 16:04:41 +02:00
'å ä ɧ 汉语 ↥ 🤦 🦄' ;
2020-01-08 09:20:53 -08:00
}, buf_lines ( 1 ))
end )
2021-02-20 04:20:42 +01:00
it ( 'applies text edits at the end of the document' , function ()
local edits = {
make_edit ( 5 , 0 , 5 , 0 , "foobar" );
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2021-02-20 04:20:42 +01:00
eq ({
'First line of text' ;
'Second line of text' ;
'Third line of text' ;
'Fourth line of text' ;
'å å ɧ 汉语 ↥ 🤦 🦄' ;
'foobar' ;
}, buf_lines ( 1 ))
end )
2022-01-13 18:28:13 +09:00
it ( 'applies multiple text edits at the end of the document' , function ()
local edits = {
make_edit ( 4 , 0 , 5 , 0 , "" );
make_edit ( 5 , 0 , 5 , 0 , "foobar" );
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2022-01-13 18:28:13 +09:00
eq ({
'First line of text' ;
'Second line of text' ;
'Third line of text' ;
'Fourth line of text' ;
'foobar' ;
}, buf_lines ( 1 ))
end )
2020-05-17 19:47:14 +02:00
2021-09-19 05:19:21 +09:00
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' )
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2021-09-19 05:19:21 +09:00
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 )
2021-10-19 03:49:33 +09:00
it ( 'fix the cursor to the valid col if the content was removed' , function ()
2021-09-19 05:19:21 +09:00
funcs.nvim_win_set_cursor ( 0 , { 2 , 6 })
local edits = {
2021-10-19 03:49:33 +09:00
make_edit ( 1 , 0 , 1 , 6 , '' ),
make_edit ( 1 , 6 , 1 , 19 , '' )
2021-09-19 05:19:21 +09:00
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2021-09-19 05:19:21 +09:00
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 )
2021-10-19 03:49:33 +09:00
it ( 'fix the cursor to the valid row if the content was removed' , function ()
funcs.nvim_win_set_cursor ( 0 , { 2 , 6 })
local edits = {
make_edit ( 1 , 0 , 1 , 6 , '' ),
make_edit ( 0 , 18 , 5 , 0 , '' )
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2021-10-19 03:49:33 +09:00
eq ({
'First line of text' ;
}, buf_lines ( 1 ))
eq ({ 1 , 6 }, funcs.nvim_win_get_cursor ( 0 ))
end )
2021-09-19 05:19:21 +09:00
it ( 'fix the cursor row' , function ()
funcs.nvim_win_set_cursor ( 0 , { 3 , 0 })
local edits = {
make_edit ( 1 , 0 , 2 , 0 , '' )
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2021-09-19 05:19:21 +09:00
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 ()
2023-03-14 20:59:43 +09:00
-- append empty last line. See #22636
exec_lua ( 'vim.api.nvim_buf_set_lines(...)' , 1 , - 1 , - 1 , true , { '' })
2021-09-19 05:19:21 +09:00
funcs.nvim_win_set_cursor ( 0 , { 2 , 11 })
local edits = {
make_edit ( 1 , 7 , 1 , 11 , '' )
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2021-09-19 05:19:21 +09:00
eq ({
'First line of text' ;
'Second of text' ;
'Third line of text' ;
'Fourth line of text' ;
'å å ɧ 汉语 ↥ 🤦 🦄' ;
2023-03-14 20:59:43 +09:00
'' ;
2021-09-19 05:19:21 +09:00
}, 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 , '' )
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2021-09-19 05:19:21 +09:00
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 )
2020-05-17 19:47:14 +02:00
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" });
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2020-05-17 19:47:14 +02:00
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" });
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2020-05-17 19:47:14 +02:00
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" });
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-16" )
2020-05-17 19:47:14 +02:00
eq ({ 'All replaced' }, buf_lines ( 1 ))
end )
end )
2020-01-08 09:20:53 -08:00
end )
2020-04-27 10:55:06 +02:00
2022-09-20 22:14:58 +02:00
describe ( 'apply_text_edits regression tests for #20116' , function ()
before_each ( function ()
insert ( dedent ( [[
Test line one
Test line two 21 char]] ))
end )
describe ( 'with LSP end column out of bounds and start column at 0' , function ()
it ( 'applies edits at the end of the buffer' , function ()
local edits = {
make_edit ( 0 , 0 , 1 , 22 , { '#include "whatever.h" \r\n #include <algorithm> \r ' });
}
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-8" )
eq ({ '#include "whatever.h"' , '#include <algorithm>' }, buf_lines ( 1 ))
end )
it ( 'applies edits in the middle of the buffer' , function ()
local edits = {
make_edit ( 0 , 0 , 0 , 22 , { '#include "whatever.h" \r\n #include <algorithm> \r ' });
}
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-8" )
eq ({ '#include "whatever.h"' , '#include <algorithm>' , 'Test line two 21 char' }, buf_lines ( 1 ))
end )
end )
describe ( 'with LSP end column out of bounds and start column NOT at 0' , function ()
it ( 'applies edits at the end of the buffer' , function ()
local edits = {
make_edit ( 0 , 2 , 1 , 22 , { '#include "whatever.h" \r\n #include <algorithm> \r ' });
}
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-8" )
eq ({ 'Te#include "whatever.h"' , '#include <algorithm>' }, buf_lines ( 1 ))
end )
it ( 'applies edits in the middle of the buffer' , function ()
local edits = {
make_edit ( 0 , 2 , 0 , 22 , { '#include "whatever.h" \r\n #include <algorithm> \r ' });
}
exec_lua ( 'vim.lsp.util.apply_text_edits(...)' , edits , 1 , "utf-8" )
eq ({ 'Te#include "whatever.h"' , '#include <algorithm>' , 'Test line two 21 char' }, buf_lines ( 1 ))
end )
end )
end )
2020-04-27 10:55:06 +02:00
describe ( 'apply_text_document_edit' , function ()
2020-05-03 13:01:00 +09:00
local target_bufnr
2020-05-14 04:14:52 +01:00
local text_document_edit = function ( editVersion )
return {
edits = {
make_edit ( 0 , 0 , 0 , 3 , "First ↥ 🤦 🦄" )
2021-01-11 11:39:11 -05:00
},
2020-05-14 04:14:52 +01:00
textDocument = {
2021-07-08 00:36:27 +01:00
uri = "file:///fake/uri" ;
2020-05-14 04:14:52 +01:00
version = editVersion
}
}
end
2020-04-27 10:55:06 +02:00
before_each ( function ()
2020-05-03 13:01:00 +09:00
target_bufnr = exec_lua [[
2021-07-08 00:36:27 +01:00
local bufnr = vim.uri_to_bufnr("file:///fake/uri")
2020-05-14 04:14:52 +01:00
local lines = {"1st line of text", "2nd line of 语text"}
2020-05-03 13:01:00 +09:00
vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
return bufnr
]]
2020-04-27 10:55:06 +02:00
end )
2020-05-03 13:01:00 +09:00
it ( 'correctly goes ahead with the edit if all is normal' , function ()
2022-01-13 02:34:04 -08:00
exec_lua ( "vim.lsp.util.apply_text_document_edit(..., nil, 'utf-16')" , text_document_edit ( 5 ))
2020-04-27 10:55:06 +02:00
eq ({
2020-05-14 04:14:52 +01:00
'First ↥ 🤦 🦄 line of text' ;
'2nd line of 语text' ;
2020-05-03 13:01:00 +09:00
}, buf_lines ( target_bufnr ))
2020-04-27 10:55:06 +02:00
end )
2021-04-14 21:29:32 +02:00
it ( 'always accepts edit with version = 0' , function ()
exec_lua ( [[
local args = {...}
local bufnr = select(1, ...)
local text_edit = select(2, ...)
vim.lsp.util.buf_versions[bufnr] = 10
2022-01-13 02:34:04 -08:00
vim.lsp.util.apply_text_document_edit(text_edit, nil, 'utf-16')
2021-04-14 21:29:32 +02:00
]] , target_bufnr , text_document_edit ( 0 ))
eq ({
'First ↥ 🤦 🦄 line of text' ;
'2nd line of 语text' ;
}, buf_lines ( target_bufnr ))
end )
2020-04-27 10:55:06 +02:00
it ( 'skips the edit if the version of the edit is behind the local buffer ' , function ()
2020-05-14 04:14:52 +01:00
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
2022-01-13 02:34:04 -08:00
vim.lsp.util.apply_text_document_edit(args[1], nil, 'utf-16')
2020-05-14 04:14:52 +01:00
]] , edit , versionedBuf )
end
local baseText = {
'1st line of text' ;
'2nd line of 语text' ;
2020-04-27 10:55:06 +02:00
}
2020-05-14 04:14:52 +01:00
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 })
2020-04-27 10:55:06 +02:00
eq ({
2020-05-14 04:14:52 +01:00
'First ↥ 🤦 🦄 line of text' ;
'2nd line of 语text' ;
2020-05-03 13:01:00 +09:00
}, buf_lines ( target_bufnr ))
2020-04-27 10:55:06 +02:00
end )
2020-01-08 09:20:53 -08:00
end )
2021-01-11 11:39:11 -05:00
2020-05-11 16:56:35 +02:00
describe ( 'workspace_apply_edit' , function ()
it ( 'workspace/applyEdit returns ApplyWorkspaceEditResponse' , function ()
2022-01-13 02:34:04 -08:00
local expected_handlers = {
{ NIL , {}, { method = "test" , client_id = 1 }};
}
test_rpc_server {
test_name = "basic_init" ;
on_init = function ( client , _ )
client.stop ()
end ;
-- If the program timed out, then code will be nil.
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2022-01-13 02:34:04 -08:00
end ;
-- Note that NIL must be used here.
-- on_handler(err, method, result, client_id)
on_handler = function (...)
local expected = {
applied = true ;
failureReason = nil ;
}
eq ( expected , exec_lua [[
local apply_edit = {
label = nil;
edit = {};
}
return vim.lsp.handlers['workspace/applyEdit'](nil, apply_edit, {client_id = TEST_RPC_CLIENT_ID})
]] )
eq ( table.remove ( expected_handlers ), {...})
end ;
2020-05-11 16:56:35 +02:00
}
end )
end )
2021-01-11 11:39:11 -05:00
describe ( 'apply_workspace_edit' , function ()
local replace_line_edit = function ( row , new_line , editVersion )
return {
edits = {
-- NOTE: This is a hack if you have a line longer than 1000 it won't replace it
make_edit ( row , 0 , row , 1000 , new_line )
},
textDocument = {
2021-07-08 00:36:27 +01:00
uri = "file:///fake/uri" ;
2021-01-11 11:39:11 -05:00
version = editVersion
}
}
end
-- Some servers send all the edits separately, but with the same version.
-- We should not stop applying the edits
local make_workspace_edit = function ( changes )
return {
documentChanges = changes
}
end
local target_bufnr , changedtick = nil , nil
before_each ( function ()
local ret = exec_lua [[
2021-07-08 00:36:27 +01:00
local bufnr = vim.uri_to_bufnr("file:///fake/uri")
2021-01-11 11:39:11 -05:00
local lines = {
"Original Line #1",
"Original Line #2"
}
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
local update_changed_tick = function()
vim.lsp.util.buf_versions[bufnr] = vim.api.nvim_buf_get_var(bufnr, 'changedtick')
end
update_changed_tick()
vim.api.nvim_buf_attach(bufnr, false, {
on_changedtick = function()
update_changed_tick()
end
})
return {bufnr, vim.api.nvim_buf_get_var(bufnr, 'changedtick')}
]]
target_bufnr = ret [ 1 ]
changedtick = ret [ 2 ]
end )
it ( 'apply_workspace_edit applies a single edit' , function ()
local new_lines = {
"First Line" ,
}
local edits = {}
for row , line in ipairs ( new_lines ) do
table.insert ( edits , replace_line_edit ( row - 1 , line , changedtick ))
end
eq ({
"First Line" ,
"Original Line #2" ,
}, exec_lua ( [[
local args = {...}
local workspace_edits = args[1]
local target_bufnr = args[2]
2022-01-13 02:34:04 -08:00
vim.lsp.util.apply_workspace_edit(workspace_edits, 'utf-16')
2021-01-11 11:39:11 -05:00
return vim.api.nvim_buf_get_lines(target_bufnr, 0, -1, false)
]] , make_workspace_edit ( edits ), target_bufnr ))
end )
it ( 'apply_workspace_edit applies multiple edits' , function ()
local new_lines = {
"First Line" ,
"Second Line" ,
}
local edits = {}
for row , line in ipairs ( new_lines ) do
table.insert ( edits , replace_line_edit ( row - 1 , line , changedtick ))
end
eq ( new_lines , exec_lua ( [[
local args = {...}
local workspace_edits = args[1]
local target_bufnr = args[2]
2022-01-13 02:34:04 -08:00
vim.lsp.util.apply_workspace_edit(workspace_edits, 'utf-16')
2021-01-11 11:39:11 -05:00
return vim.api.nvim_buf_get_lines(target_bufnr, 0, -1, false)
]] , make_workspace_edit ( edits ), target_bufnr ))
end )
2021-03-18 11:07:06 +01:00
it ( 'Supports file creation with CreateFile payload' , function ()
local tmpfile = helpers.tmpname ()
os.remove ( tmpfile ) -- Should not exist, only interested in a tmpname
local uri = exec_lua ( 'return vim.uri_from_fname(...)' , tmpfile )
local edit = {
documentChanges = {
{
kind = 'create' ,
uri = uri ,
},
}
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_workspace_edit(...)' , edit , 'utf-16' )
2021-03-18 11:07:06 +01:00
eq ( true , exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , tmpfile ))
end )
2022-09-24 06:46:21 -04:00
it ( 'Supports file creation in folder that needs to be created with CreateFile payload' , function ()
local tmpfile = helpers.tmpname ()
os.remove ( tmpfile ) -- Should not exist, only interested in a tmpname
tmpfile = tmpfile .. '/dummy/x/'
local uri = exec_lua ( 'return vim.uri_from_fname(...)' , tmpfile )
local edit = {
documentChanges = {
{
kind = 'create' ,
uri = uri ,
},
}
}
exec_lua ( 'vim.lsp.util.apply_workspace_edit(...)' , edit , 'utf-16' )
eq ( true , exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , tmpfile ))
end )
2021-03-18 11:07:06 +01:00
it ( 'createFile does not touch file if it exists and ignoreIfExists is set' , function ()
local tmpfile = helpers.tmpname ()
write_file ( tmpfile , 'Dummy content' )
local uri = exec_lua ( 'return vim.uri_from_fname(...)' , tmpfile )
local edit = {
documentChanges = {
{
kind = 'create' ,
uri = uri ,
options = {
ignoreIfExists = true ,
},
},
}
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_workspace_edit(...)' , edit , 'utf-16' )
2021-03-18 11:07:06 +01:00
eq ( true , exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , tmpfile ))
eq ( 'Dummy content' , read_file ( tmpfile ))
end )
it ( 'createFile overrides file if overwrite is set' , function ()
local tmpfile = helpers.tmpname ()
write_file ( tmpfile , 'Dummy content' )
local uri = exec_lua ( 'return vim.uri_from_fname(...)' , tmpfile )
local edit = {
documentChanges = {
{
kind = 'create' ,
uri = uri ,
options = {
overwrite = true ,
ignoreIfExists = true , -- overwrite must win over ignoreIfExists
},
},
}
}
2022-01-13 02:34:04 -08:00
exec_lua ( 'vim.lsp.util.apply_workspace_edit(...)' , edit , 'utf-16' )
2021-03-18 11:07:06 +01:00
eq ( true , exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , tmpfile ))
eq ( '' , read_file ( tmpfile ))
end )
2021-03-18 11:32:33 +01:00
it ( 'DeleteFile delete file and buffer' , function ()
local tmpfile = helpers.tmpname ()
write_file ( tmpfile , 'Be gone' )
local uri = exec_lua ( [[
local fname = select(1, ...)
local bufnr = vim.fn.bufadd(fname)
vim.fn.bufload(bufnr)
return vim.uri_from_fname(fname)
]] , tmpfile )
local edit = {
documentChanges = {
{
kind = 'delete' ,
uri = uri ,
}
}
}
2022-01-13 02:34:04 -08:00
eq ( true , pcall ( exec_lua , 'vim.lsp.util.apply_workspace_edit(...)' , edit , 'utf-16' ))
2021-03-18 11:32:33 +01:00
eq ( false , exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , tmpfile ))
eq ( false , exec_lua ( 'return vim.api.nvim_buf_is_loaded(vim.fn.bufadd(...))' , tmpfile ))
end )
it ( 'DeleteFile fails if file does not exist and ignoreIfNotExists is false' , function ()
local tmpfile = helpers.tmpname ()
os.remove ( tmpfile )
local uri = exec_lua ( 'return vim.uri_from_fname(...)' , tmpfile )
local edit = {
documentChanges = {
{
kind = 'delete' ,
uri = uri ,
options = {
ignoreIfNotExists = false ,
}
}
}
}
eq ( false , pcall ( exec_lua , 'vim.lsp.util.apply_workspace_edit(...)' , edit ))
eq ( false , exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , tmpfile ))
end )
2021-01-11 11:39:11 -05:00
end )
2020-04-20 18:35:54 +03:00
describe ( 'completion_list_to_complete_items' , function ()
2020-04-20 20:20:14 +03:00
-- 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 ()
2020-04-20 18:35:54 +03:00
local prefix = 'foo'
local completion_list = {
-- resolves into label
2020-08-31 01:29:47 -04:00
{ label = 'foobar' , sortText = "a" },
{ label = 'foobar' , sortText = "b" , textEdit = {} },
2020-04-20 18:35:54 +03:00
-- resolves into insertText
2020-08-31 01:29:47 -04:00
{ label = 'foocar' , sortText = "c" , insertText = 'foobar' },
{ label = 'foocar' , sortText = "d" , insertText = 'foobar' , textEdit = {} },
2020-04-20 18:35:54 +03:00
-- resolves into textEdit.newText
2020-08-31 01:29:47 -04:00
{ label = 'foocar' , sortText = "e" , insertText = 'foodar' , textEdit = { newText = 'foobar' } },
{ label = 'foocar' , sortText = "f" , textEdit = { newText = 'foobar' } },
2020-05-28 14:31:56 +02:00
-- real-world snippet text
2021-05-16 01:07:45 -07:00
{ label = 'foocar' , sortText = "g" , insertText = 'foodar' , insertTextFormat = 2 , textEdit = { newText = 'foobar(${1:place holder}, ${2:more ...holder{ \\ }})' } },
{ label = 'foocar' , sortText = "h" , insertText = 'foodar(${1:var1} typ1, ${2:var2} *typ2) {$0 \\ }' , insertTextFormat = 2 , textEdit = {} },
2020-05-28 14:31:56 +02:00
-- nested snippet tokens
2021-05-16 01:07:45 -07:00
{ label = 'foocar' , sortText = "i" , insertText = 'foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0 \\ }' , insertTextFormat = 2 , textEdit = {} },
2021-09-14 20:31:41 +09:00
-- braced tabstop
{ label = 'foocar' , sortText = "j" , insertText = 'foodar()${0}' , insertTextFormat = 2 , textEdit = {} },
2020-05-28 14:31:56 +02:00
-- plain text
2021-09-14 20:31:41 +09:00
{ label = 'foocar' , sortText = "k" , insertText = 'foodar(${1:var1})' , insertTextFormat = 1 , textEdit = {} },
2020-04-20 18:35:54 +03:00
}
local completion_list_items = { items = completion_list }
local expected = {
2020-08-31 01:29:47 -04:00
{ abbr = 'foobar' , dup = 1 , empty = 1 , icase = 1 , info = ' ' , kind = 'Unknown' , menu = '' , word = 'foobar' , user_data = { nvim = { lsp = { completion_item = { label = 'foobar' , sortText = "a" } } } } },
{ abbr = 'foobar' , dup = 1 , empty = 1 , icase = 1 , info = ' ' , kind = 'Unknown' , menu = '' , word = 'foobar' , user_data = { nvim = { lsp = { completion_item = { label = 'foobar' , sortText = "b" , textEdit = {} } } } } },
{ abbr = 'foocar' , dup = 1 , empty = 1 , icase = 1 , info = ' ' , kind = 'Unknown' , menu = '' , word = 'foobar' , user_data = { nvim = { lsp = { completion_item = { label = 'foocar' , sortText = "c" , insertText = 'foobar' } } } } },
{ abbr = 'foocar' , dup = 1 , empty = 1 , icase = 1 , info = ' ' , kind = 'Unknown' , menu = '' , word = 'foobar' , user_data = { nvim = { lsp = { completion_item = { label = 'foocar' , sortText = "d" , 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' , sortText = "e" , 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' , sortText = "f" , textEdit = { newText = 'foobar' } } } } } },
2021-05-16 01:07:45 -07:00
{ 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 = {} } } } } },
2021-09-14 20:31:41 +09:00
{ 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 = {} } } } } },
2020-04-20 18:35:54 +03:00
}
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 )
2020-11-12 22:21:34 -05:00
2021-03-15 22:34:22 +01:00
describe ( 'lsp.util.rename' , function ()
2023-02-18 09:43:59 +03:00
local pathsep = helpers.get_pathsep ()
2021-03-15 22:34:22 +01:00
it ( 'Can rename an existing file' , function ()
local old = helpers.tmpname ()
write_file ( old , 'Test content' )
local new = helpers.tmpname ()
os.remove ( new ) -- only reserve the name, file must not exist for the test scenario
local lines = exec_lua ( [[
local old = select(1, ...)
local new = select(2, ...)
vim.lsp.util.rename(old, new)
-- after rename the target file must have the contents of the source file
local bufnr = vim.fn.bufadd(new)
2021-11-14 12:55:16 +01:00
vim.fn.bufload(new)
2021-03-15 22:34:22 +01:00
return vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
]] , old , new )
eq ({ 'Test content' }, lines )
local exists = exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , old )
eq ( false , exists )
exists = exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , new )
eq ( true , exists )
os.remove ( new )
end )
2023-03-14 13:08:37 +01:00
it ( "Kills old buffer after renaming an existing file" , function ()
local old = helpers.tmpname ()
write_file ( old , 'Test content' )
local new = helpers.tmpname ()
os.remove ( new ) -- only reserve the name, file must not exist for the test scenario
local lines = exec_lua ( [[
local old = select(1, ...)
local oldbufnr = vim.fn.bufadd(old)
local new = select(2, ...)
vim.lsp.util.rename(old, new)
return vim.fn.bufloaded(oldbufnr)
]] , old , new )
eq ( 0 , lines )
os.remove ( new )
end )
it ( 'Can rename a directory' , function ()
2023-02-18 09:43:59 +03:00
-- only reserve the name, file must not exist for the test scenario
local old_dir = helpers.tmpname ()
local new_dir = helpers.tmpname ()
os.remove ( old_dir )
os.remove ( new_dir )
helpers.mkdir_p ( old_dir )
2023-03-14 13:08:37 +01:00
local file = 'file.txt'
2023-02-18 09:43:59 +03:00
write_file ( old_dir .. pathsep .. file , 'Test content' )
2023-03-14 13:08:37 +01:00
local lines = exec_lua ( [[
2023-02-18 09:43:59 +03:00
local old_dir = select(1, ...)
local new_dir = select(2, ...)
2023-03-14 13:08:37 +01:00
local pathsep = select(3, ...)
local oldbufnr = vim.fn.bufadd(old_dir .. pathsep .. 'file')
2023-02-18 09:43:59 +03:00
vim.lsp.util.rename(old_dir, new_dir)
2023-03-14 13:08:37 +01:00
return vim.fn.bufloaded(oldbufnr)
]] , old_dir , new_dir , pathsep )
eq ( 0 , lines )
2023-02-18 09:43:59 +03:00
eq ( false , exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , old_dir ))
eq ( true , exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , new_dir ))
eq ( true , exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , new_dir .. pathsep .. file ))
eq ( 'Test content' , read_file ( new_dir .. pathsep .. file ))
os.remove ( new_dir )
end )
2021-03-15 22:34:22 +01:00
it ( 'Does not rename file if target exists and ignoreIfExists is set or overwrite is false' , function ()
local old = helpers.tmpname ()
write_file ( old , 'Old File' )
local new = helpers.tmpname ()
write_file ( new , 'New file' )
exec_lua ( [[
local old = select(1, ...)
local new = select(2, ...)
vim.lsp.util.rename(old, new, { ignoreIfExists = true })
]] , old , new )
eq ( true , exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , old ))
eq ( 'New file' , read_file ( new ))
exec_lua ( [[
local old = select(1, ...)
local new = select(2, ...)
vim.lsp.util.rename(old, new, { overwrite = false })
]] , old , new )
eq ( true , exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , old ))
eq ( 'New file' , read_file ( new ))
end )
it ( 'Does override target if overwrite is true' , function ()
local old = helpers.tmpname ()
write_file ( old , 'Old file' )
local new = helpers.tmpname ()
write_file ( new , 'New file' )
exec_lua ( [[
local old = select(1, ...)
local new = select(2, ...)
vim.lsp.util.rename(old, new, { overwrite = true })
]] , old , new )
eq ( false , exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , old ))
eq ( true , exec_lua ( 'return vim.loop.fs_stat(...) ~= nil' , new ))
eq ( 'Old file \n ' , read_file ( new ))
end )
end )
2020-06-30 17:48:04 +02:00
describe ( 'lsp.util.locations_to_items' , function ()
it ( 'Convert Location[] to items' , function ()
local expected = {
{
2021-07-08 00:36:27 +01:00
filename = '/fake/uri' ,
2020-06-30 17:48:04 +02:00
lnum = 1 ,
col = 3 ,
text = 'testing'
},
}
local actual = exec_lua [[
2021-07-08 00:36:27 +01:00
local bufnr = vim.uri_to_bufnr("file:///fake/uri")
2020-06-30 17:48:04 +02:00
local lines = {"testing", "123"}
vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
local locations = {
{
2021-07-08 00:36:27 +01:00
uri = 'file:///fake/uri',
2020-06-30 17:48:04 +02:00
range = {
start = { line = 0, character = 2 },
['end'] = { line = 0, character = 3 },
}
},
}
2022-01-13 02:34:04 -08:00
return vim.lsp.util.locations_to_items(locations, 'utf-16')
2020-06-30 17:48:04 +02:00
]]
eq ( expected , actual )
end )
it ( 'Convert LocationLink[] to items' , function ()
local expected = {
{
2021-07-08 00:36:27 +01:00
filename = '/fake/uri' ,
2020-06-30 17:48:04 +02:00
lnum = 1 ,
col = 3 ,
text = 'testing'
},
}
local actual = exec_lua [[
2021-07-08 00:36:27 +01:00
local bufnr = vim.uri_to_bufnr("file:///fake/uri")
2020-06-30 17:48:04 +02:00
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 },
}
},
}
2022-01-13 02:34:04 -08:00
return vim.lsp.util.locations_to_items(locations, 'utf-16')
2020-06-30 17:48:04 +02:00
]]
eq ( expected , actual )
end )
end )
2020-05-02 15:09:49 +09:00
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 )
2020-06-12 02:34:34 +02:00
it ( 'convert SymbolInformation[] to items' , function ()
2020-05-02 15:09:49 +09:00
local expected = {
{
col = 1 ,
2021-07-08 00:36:27 +01:00
filename = '/test_a' ,
2020-05-02 15:09:49 +09:00
kind = 'File' ,
lnum = 2 ,
text = '[File] TestA'
},
{
col = 1 ,
2021-07-08 00:36:27 +01:00
filename = '/test_b' ,
2020-05-02 15:09:49 +09:00
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
}
},
2021-07-08 00:36:27 +01:00
uri = "file:///test_a"
2020-05-02 15:09:49 +09:00
},
2022-10-23 10:17:45 +02:00
containerName = "TestAContainer"
2020-05-02 15:09:49 +09:00
},
{
deprecated = false,
kind = 2,
name = "TestB",
location = {
range = {
start = {
character = 0,
line = 3
},
["end"] = {
character = 0,
line = 4
}
},
2021-07-08 00:36:27 +01:00
uri = "file:///test_b"
2020-05-02 15:09:49 +09:00
},
2022-10-23 10:17:45 +02:00
containerName = "TestBContainer"
2020-05-02 15:09:49 +09:00
}
}
return vim.lsp.util.symbols_to_items(sym_info, nil)
]] )
end )
end )
2020-05-08 05:23:25 +09:00
describe ( 'lsp.util._get_completion_item_kind_name' , function ()
2020-06-12 02:34:34 +02:00
it ( 'returns the name specified by protocol' , function ()
2020-05-08 05:23:25 +09:00
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 )
2020-06-12 02:34:34 +02:00
it ( 'returns the name not specified by protocol' , function ()
2020-05-08 05:23:25 +09:00
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 ()
2020-06-12 02:34:34 +02:00
it ( 'returns the name specified by protocol' , function ()
2020-05-08 05:23:25 +09:00
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 )
2020-06-12 02:34:34 +02:00
it ( 'returns the name not specified by protocol' , function ()
2020-05-08 05:23:25 +09:00
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 )
2020-05-19 08:51:10 +02:00
describe ( 'lsp.util.jump_to_location' , function ()
2021-05-19 15:33:32 +02:00
local target_bufnr
2021-04-23 18:50:35 -03:00
2021-05-19 15:33:32 +02:00
before_each ( function ()
target_bufnr = exec_lua [[
2021-07-08 00:36:27 +01:00
local bufnr = vim.uri_to_bufnr("file:///fake/uri")
2021-05-19 15:33:32 +02:00
local lines = {"1st line of text", "å å ɧ 汉语 ↥ 🤦 🦄"}
2020-05-19 08:51:10 +02:00
vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
return bufnr
2021-05-19 15:33:32 +02:00
]]
2020-05-19 08:51:10 +02:00
end )
2021-05-19 15:33:32 +02:00
local location = function ( start_line , start_char , end_line , end_char )
2020-05-19 08:51:10 +02:00
return {
2021-07-08 00:36:27 +01:00
uri = "file:///fake/uri" ,
2020-05-19 08:51:10 +02:00
range = {
start = { line = start_line , character = start_char },
[ "end" ] = { line = end_line , character = end_char },
},
}
end
2021-05-19 15:33:32 +02:00
local jump = function ( msg )
2022-01-13 02:34:04 -08:00
eq ( true , exec_lua ( 'return vim.lsp.util.jump_to_location(...)' , msg , "utf-16" ))
2021-05-19 15:33:32 +02:00
eq ( target_bufnr , exec_lua [[return vim.fn.bufnr('%')]] )
2020-05-19 08:51:10 +02:00
return {
line = exec_lua [[return vim.fn.line('.')]] ,
col = exec_lua [[return vim.fn.col('.')]] ,
}
end
it ( 'jumps to a Location' , function ()
2021-05-19 15:33:32 +02:00
local pos = jump ( location ( 0 , 9 , 0 , 9 ))
2020-05-19 08:51:10 +02:00
eq ( 1 , pos.line )
eq ( 10 , pos.col )
end )
it ( 'jumps to a LocationLink' , function ()
2021-05-19 15:33:32 +02:00
local pos = jump ({
2021-07-08 00:36:27 +01:00
targetUri = "file:///fake/uri" ,
2020-05-19 08:51:10 +02:00
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 ()
2021-05-19 15:33:32 +02:00
local pos = jump ( location ( 1 , 2 , 1 , 2 ))
2020-05-19 08:51:10 +02:00
eq ( 2 , pos.line )
eq ( 4 , pos.col )
eq ( 'å' , exec_lua [[return vim.fn.expand('<cword>')]] )
end )
2021-04-23 18:50:35 -03:00
2021-05-06 17:51:25 -04:00
it ( 'adds current position to jumplist before jumping' , function ()
2021-05-19 15:35:30 +02:00
funcs.nvim_win_set_buf ( 0 , target_bufnr )
local mark = funcs.nvim_buf_get_mark ( target_bufnr , "'" )
eq ({ 1 , 0 }, mark )
2022-10-08 05:22:25 -03:00
funcs.nvim_win_set_cursor ( 0 , { 2 , 3 })
2021-05-19 15:35:30 +02:00
jump ( location ( 0 , 9 , 0 , 9 ))
2021-05-06 17:51:25 -04:00
2021-05-19 15:35:30 +02:00
mark = funcs.nvim_buf_get_mark ( target_bufnr , "'" )
eq ({ 2 , 3 }, mark )
2021-05-06 17:51:25 -04:00
end )
2020-05-19 08:51:10 +02:00
end )
2020-06-04 20:23:03 +02:00
2022-10-08 05:22:25 -03:00
describe ( 'lsp.util.show_document' , function ()
local target_bufnr
local target_bufnr2
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
]] )
target_bufnr2 = exec_lua ( [[
local bufnr = vim.uri_to_bufnr("file:///fake/uri2")
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 , second_uri )
return {
uri = second_uri and 'file:///fake/uri2' or 'file:///fake/uri' ,
range = {
start = { line = start_line , character = start_char },
[ 'end' ] = { line = end_line , character = end_char },
},
}
end
local show_document = function ( msg , focus , reuse_win )
eq (
true ,
exec_lua (
'return vim.lsp.util.show_document(...)' ,
msg ,
'utf-16' ,
{ reuse_win = reuse_win , focus = focus }
)
)
if focus == true or focus == nil then
eq ( target_bufnr , exec_lua ( [[return vim.fn.bufnr('%')]] ))
end
return {
line = exec_lua ( [[return vim.fn.line('.')]] ),
col = exec_lua ( [[return vim.fn.col('.')]] ),
}
end
it ( 'jumps to a Location if focus is true' , function ()
local pos = show_document ( location ( 0 , 9 , 0 , 9 ), true , true )
eq ( 1 , pos.line )
eq ( 10 , pos.col )
end )
2022-12-04 15:57:46 +01:00
it ( 'jumps to a Location if focus is true via handler' , function ()
exec_lua ( create_server_definition )
local result = exec_lua ( [[
local server = _create_server()
local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
local result = {
uri = 'file:///fake/uri',
selection = {
start = { line = 0, character = 9 },
['end'] = { line = 0, character = 9 }
},
takeFocus = true,
}
local ctx = {
client_id = client_id,
method = 'window/showDocument',
}
vim.lsp.handlers['window/showDocument'](nil, result, ctx)
vim.lsp.stop_client(client_id)
return {
cursor = vim.api.nvim_win_get_cursor(0)
}
]] )
eq ( 1 , result.cursor [ 1 ])
eq ( 9 , result.cursor [ 2 ])
end )
2022-10-08 05:22:25 -03:00
it ( 'jumps to a Location if focus not set' , function ()
local pos = show_document ( location ( 0 , 9 , 0 , 9 ), nil , true )
eq ( 1 , pos.line )
eq ( 10 , pos.col )
end )
it ( 'does not add current position to jumplist if not focus' , function ()
funcs.nvim_win_set_buf ( 0 , target_bufnr )
local mark = funcs.nvim_buf_get_mark ( target_bufnr , "'" )
eq ({ 1 , 0 }, mark )
funcs.nvim_win_set_cursor ( 0 , { 2 , 3 })
show_document ( location ( 0 , 9 , 0 , 9 ), false , true )
show_document ( location ( 0 , 9 , 0 , 9 , true ), false , true )
mark = funcs.nvim_buf_get_mark ( target_bufnr , "'" )
eq ({ 1 , 0 }, mark )
end )
it ( 'does not change cursor position if not focus and not reuse_win' , function ()
funcs.nvim_win_set_buf ( 0 , target_bufnr )
local cursor = funcs.nvim_win_get_cursor ( 0 )
show_document ( location ( 0 , 9 , 0 , 9 ), false , false )
eq ( cursor , funcs.nvim_win_get_cursor ( 0 ))
end )
it ( 'does not change window if not focus' , function ()
funcs.nvim_win_set_buf ( 0 , target_bufnr )
local win = funcs.nvim_get_current_win ()
-- same document/bufnr
show_document ( location ( 0 , 9 , 0 , 9 ), false , true )
eq ( win , funcs.nvim_get_current_win ())
-- different document/bufnr, new window/split
show_document ( location ( 0 , 9 , 0 , 9 , true ), false , true )
eq ( 2 , # funcs.nvim_list_wins ())
eq ( win , funcs.nvim_get_current_win ())
end )
it ( "respects 'reuse_win' parameter" , function ()
funcs.nvim_win_set_buf ( 0 , target_bufnr )
-- does not create a new window if the buffer is already open
show_document ( location ( 0 , 9 , 0 , 9 ), false , true )
eq ( 1 , # funcs.nvim_list_wins ())
-- creates a new window even if the buffer is already open
show_document ( location ( 0 , 9 , 0 , 9 ), false , false )
eq ( 2 , # funcs.nvim_list_wins ())
end )
it ( 'correctly sets the cursor of the split if range is given without focus' , function ()
funcs.nvim_win_set_buf ( 0 , target_bufnr )
show_document ( location ( 0 , 9 , 0 , 9 , true ), false , true )
local wins = funcs.nvim_list_wins ()
eq ( 2 , # wins )
table.sort ( wins )
eq ({ 1 , 0 }, funcs.nvim_win_get_cursor ( wins [ 1 ]))
eq ({ 1 , 9 }, funcs.nvim_win_get_cursor ( wins [ 2 ]))
end )
it ( 'does not change cursor of the split if not range and not focus' , function ()
funcs.nvim_win_set_buf ( 0 , target_bufnr )
funcs.nvim_win_set_cursor ( 0 , { 2 , 3 })
exec_lua ( [[vim.cmd.new()]] )
funcs.nvim_win_set_buf ( 0 , target_bufnr2 )
funcs.nvim_win_set_cursor ( 0 , { 2 , 3 })
show_document ({ uri = 'file:///fake/uri2' }, false , true )
local wins = funcs.nvim_list_wins ()
eq ( 2 , # wins )
eq ({ 2 , 3 }, funcs.nvim_win_get_cursor ( wins [ 1 ]))
eq ({ 2 , 3 }, funcs.nvim_win_get_cursor ( wins [ 2 ]))
end )
it ( 'respects existing buffers' , function ()
funcs.nvim_win_set_buf ( 0 , target_bufnr )
local win = funcs.nvim_get_current_win ()
exec_lua ( [[vim.cmd.new()]] )
funcs.nvim_win_set_buf ( 0 , target_bufnr2 )
funcs.nvim_win_set_cursor ( 0 , { 2 , 3 })
local split = funcs.nvim_get_current_win ()
-- reuse win for open document/bufnr if called from split
show_document ( location ( 0 , 9 , 0 , 9 , true ), false , true )
eq ({ 1 , 9 }, funcs.nvim_win_get_cursor ( split ))
eq ( 2 , # funcs.nvim_list_wins ())
funcs.nvim_set_current_win ( win )
-- reuse win for open document/bufnr if called outside the split
show_document ( location ( 0 , 9 , 0 , 9 , true ), false , true )
eq ({ 1 , 9 }, funcs.nvim_win_get_cursor ( split ))
eq ( 2 , # funcs.nvim_list_wins ())
end )
end )
2020-06-04 20:23:03 +02:00
describe ( 'lsp.util._make_floating_popup_size' , function ()
2020-06-12 02:34:34 +02:00
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 )
2020-06-04 20:23:03 +02:00
end )
2020-05-05 17:23:45 +02:00
2021-08-10 20:52:30 +02:00
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 )
2022-01-13 10:47:36 +01:00
describe ( 'lsp.util.convert_signature_help_to_markdown_lines' , function ()
it ( 'can handle negative activeSignature' , function ()
local result = exec_lua [[
local signature_help = {
activeParameter = 0,
activeSignature = -1,
signatures = {
{
documentation = "",
label = "TestEntity.TestEntity()",
parameters = {}
},
}
}
return vim.lsp.util.convert_signature_help_to_markdown_lines(signature_help, 'cs', {','})
]]
local expected = { '```cs' , 'TestEntity.TestEntity()' , '```' , '' }
eq ( expected , result )
end )
end )
2020-05-05 17:23:45 +02:00
describe ( 'lsp.util.get_effective_tabstop' , function ()
2022-03-20 13:41:46 -04:00
local function test_tabstop ( tabsize , shiftwidth )
2020-05-05 17:23:45 +02:00
exec_lua ( string.format ( [[
2022-03-20 13:41:46 -04:00
vim.api.nvim_buf_set_option(0, 'shiftwidth', %d)
2020-05-05 17:23:45 +02:00
vim.api.nvim_buf_set_option(0, 'tabstop', 2)
2022-03-20 13:41:46 -04:00
]] , shiftwidth ))
2020-05-05 17:23:45 +02:00
eq ( tabsize , exec_lua ( 'return vim.lsp.util.get_effective_tabstop()' ))
end
2022-03-20 13:41:46 -04:00
it ( 'with shiftwidth = 1' , function () test_tabstop ( 1 , 1 ) end )
it ( 'with shiftwidth = 0' , function () test_tabstop ( 2 , 0 ) end )
2020-05-05 17:23:45 +02:00
end )
2020-07-18 21:10:09 +02:00
describe ( 'vim.lsp.buf.outgoing_calls' , function ()
it ( 'does nothing for an empty response' , function ()
local qflist_count = exec_lua ( [=[
2021-08-28 00:12:30 -04:00
require'vim.lsp.handlers'['callHierarchy/outgoingCalls'](nil, nil, {}, nil)
2020-07-18 21:10:09 +02:00
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"
}
} }
2021-02-23 00:02:51 +01:00
local handler = require'vim.lsp.handlers'['callHierarchy/outgoingCalls']
2021-08-28 00:12:30 -04:00
handler(nil, rust_analyzer_response, {})
2020-07-18 21:10:09 +02:00
return vim.fn.getqflist()
]=] )
local expected = { {
bufnr = 2 ,
col = 5 ,
2021-07-31 16:49:25 -04:00
end_col = 0 ,
2020-07-18 21:10:09 +02:00
lnum = 4 ,
2021-07-31 16:49:25 -04:00
end_lnum = 0 ,
2020-07-18 21:10:09 +02:00
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 ( [=[
2021-08-28 00:12:30 -04:00
require'vim.lsp.handlers'['callHierarchy/incomingCalls'](nil, nil, {})
2020-07-18 21:10:09 +02:00
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
}
} }
} }
2021-02-23 00:02:51 +01:00
local handler = require'vim.lsp.handlers'['callHierarchy/incomingCalls']
2021-08-28 00:12:30 -04:00
handler(nil, rust_analyzer_response, {})
2020-07-18 21:10:09 +02:00
return vim.fn.getqflist()
]=] )
local expected = { {
bufnr = 2 ,
col = 5 ,
2021-07-31 16:49:25 -04:00
end_col = 0 ,
2020-07-18 21:10:09 +02:00
lnum = 4 ,
2021-07-31 16:49:25 -04:00
end_lnum = 0 ,
2020-07-18 21:10:09 +02:00
module = "" ,
nr = 0 ,
pattern = "" ,
text = "main" ,
type = "" ,
valid = 1 ,
vcol = 0
} }
eq ( expected , qflist )
end )
end )
2021-09-08 23:00:15 +08:00
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 , {}, { method = "start" , client_id = 1 }};
},
},
}) do
it ( test.it , function ()
local client
test_rpc_server {
test_name = test.name ;
on_init = function ( _client )
client = _client
2022-04-30 02:22:30 -07:00
eq ( true , client.server_capabilities (). renameProvider.prepareProvider )
2021-09-08 23:00:15 +08:00
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 = {}
2021-11-07 16:13:53 +01:00
vim.fn.input = function(opts, on_confirm)
vim.lsp._stubs.input_prompt = opts.prompt
vim.lsp._stubs.input_text = opts.default
2021-09-08 23:00:15 +08:00
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 )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2021-09-08 23:00:15 +08:00
end ;
on_handler = function ( err , result , ctx )
2021-03-11 23:49:59 +01:00
-- 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
2021-09-08 23:00:15 +08:00
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 )
2021-03-12 10:19:21 +01:00
describe ( 'vim.lsp.buf.code_action' , function ()
it ( 'Calls client side command if available' , function ()
2021-09-28 23:04:01 +02:00
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 )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2021-09-28 23:04:01 +02:00
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
2021-03-12 10:19:21 +01:00
end
2021-09-28 23:04:01 +02:00
}
2021-03-12 10:19:21 +01:00
end )
2022-06-05 16:43:32 +02:00
it ( 'Calls workspace/executeCommand if no client side command' , function ()
local client
local expected_handlers = {
{ NIL , {}, { method = 'shutdown' , client_id = 1 } },
{
NIL ,
{ command = 'dummy1' , title = 'Command 1' },
{ bufnr = 1 , method = 'workspace/executeCommand' , client_id = 1 },
},
{ NIL , {}, { method = 'start' , client_id = 1 } },
}
test_rpc_server ({
test_name = 'code_action_server_side_command' ,
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 )
ctx.params = nil -- don't compare in assert
eq ( table.remove ( expected_handlers ), { err , result , ctx })
if ctx.method == 'start' then
exec_lua ( [[
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
client.stop ()
end
end ,
})
end )
2022-04-30 10:14:31 +02:00
it ( 'Filters and automatically applies action if requested' , 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_filter' ,
on_init = function ( client_ )
client = client_
end ,
on_setup = function ()
end ,
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2022-04-30 10:14:31 +02:00
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['preferred_command'] = function(cmd)
vim.lsp.commands['executed_preferred'] = function()
end
end
2022-05-12 18:48:02 +02:00
vim.lsp.commands['quickfix_command'] = function(cmd)
vim.lsp.commands['executed_quickfix'] = function()
end
end
2022-04-30 10:14:31 +02:00
local bufnr = vim.api.nvim_get_current_buf()
vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
vim.lsp.buf.code_action({ filter = function(a) return a.isPreferred end, apply = true, })
2022-05-12 18:48:02 +02:00
vim.lsp.buf.code_action({
-- expect to be returned actions 'quickfix' and 'quickfix.foo'
context = { only = {'quickfix'}, },
apply = true,
filter = function(a)
if a.kind == 'quickfix.foo' then
vim.lsp.commands['filtered_quickfix_foo'] = function() end
return false
elseif a.kind == 'quickfix' then
return true
else
assert(nil, 'unreachable')
end
end,
})
2022-04-30 10:14:31 +02:00
]] )
elseif ctx.method == 'shutdown' then
eq ( 'function' , exec_lua [[return type(vim.lsp.commands['executed_preferred'])]] )
2022-05-12 18:48:02 +02:00
eq ( 'function' , exec_lua [[return type(vim.lsp.commands['filtered_quickfix_foo'])]] )
eq ( 'function' , exec_lua [[return type(vim.lsp.commands['executed_quickfix'])]] )
2022-04-30 10:14:31 +02:00
client.stop ()
end
end
}
end )
2021-03-12 10:19:21 +01:00
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 )
2021-11-01 03:14:59 -07:00
describe ( 'vim.lsp.codelens' , function ()
it ( 'uses client commands' , function ()
local client
local expected_handlers = {
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
}
test_rpc_server {
test_name = 'clientside_commands' ,
on_init = function ( client_ )
client = client_
end ,
on_setup = function ()
end ,
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2021-11-01 03:14:59 -07:00
end ,
on_handler = function ( err , result , ctx )
eq ( table.remove ( expected_handlers ), { err , result , ctx })
if ctx.method == 'start' then
local fake_uri = "file:///fake/uri"
local cmd = exec_lua ( [[
fake_uri = ...
local bufnr = vim.uri_to_bufnr(fake_uri)
vim.fn.bufload(bufnr)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {'One line'})
local lenses = {
{
range = {
start = { line = 0, character = 0, },
['end'] = { line = 0, character = 8 }
},
command = { title = 'Lens1', command = 'Dummy' }
},
}
vim.lsp.codelens.on_codelens(nil, lenses, {method='textDocument/codeLens', client_id=1, bufnr=bufnr})
local cmd_called = nil
vim.lsp.commands['Dummy'] = function(command)
cmd_called = command
end
vim.api.nvim_set_current_buf(bufnr)
vim.lsp.codelens.run()
return cmd_called
]] , fake_uri )
eq ({ command = 'Dummy' , title = 'Lens1' }, cmd )
elseif ctx.method == 'shutdown' then
client.stop ()
end
end
}
end )
2022-05-05 18:50:12 +02:00
it ( 'releases buffer refresh lock' , function ()
local client
local expected_handlers = {
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
}
test_rpc_server {
test_name = 'codelens_refresh_lock' ,
on_init = function ( client_ )
client = client_
end ,
on_setup = function ()
exec_lua ( [=[
local bufnr = vim.api.nvim_get_current_buf()
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {'One line'})
vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
CALLED = false
RESPONSE = nil
local on_codelens = vim.lsp.codelens.on_codelens
vim.lsp.codelens.on_codelens = function (err, result, ...)
CALLED = true
RESPONSE = { err = err, result = result }
return on_codelens(err, result, ...)
end
]=] )
end ,
on_exit = function ( code , signal )
2022-05-30 02:40:17 -07:00
eq ( 0 , code , "exit code" )
eq ( 0 , signal , "exit signal" )
2022-05-05 18:50:12 +02:00
end ,
on_handler = function ( err , result , ctx )
eq ( table.remove ( expected_handlers ), { err , result , ctx })
if ctx.method == 'start' then
-- 1. first codelens request errors
local response = exec_lua ( [=[
CALLED = false
vim.lsp.codelens.refresh()
vim.wait(100, function () return CALLED end)
return RESPONSE
]=] )
eq ( { err = { code = - 32002 , message = "ServerNotInitialized" } }, response )
-- 2. second codelens request runs
response = exec_lua ( [=[
CALLED = false
local cmd_called = nil
vim.lsp.commands["Dummy"] = function (command)
cmd_called = command
end
vim.lsp.codelens.refresh()
vim.wait(100, function () return CALLED end)
vim.lsp.codelens.run()
vim.wait(100, function () return cmd_called end)
return cmd_called
]=] )
eq ( { command = "Dummy" , title = "Lens1" }, response )
-- 3. third codelens request runs
response = exec_lua ( [=[
CALLED = false
local cmd_called = nil
vim.lsp.commands["Dummy"] = function (command)
cmd_called = command
end
vim.lsp.codelens.refresh()
vim.wait(100, function () return CALLED end)
vim.lsp.codelens.run()
vim.wait(100, function () return cmd_called end)
return cmd_called
]=] )
eq ( { command = "Dummy" , title = "Lens2" }, response )
elseif ctx.method == 'shutdown' then
client.stop ()
end
end
}
end )
2021-11-01 03:14:59 -07:00
end )
2022-04-30 06:36:40 -07:00
describe ( "vim.lsp.buf.format" , function ()
it ( "Aborts with notify if no client matches filter" , function ()
local client
test_rpc_server {
test_name = "basic_init" ,
on_init = function ( c )
client = c
end ,
on_handler = function ()
local notify_msg = exec_lua ( [[
local bufnr = vim.api.nvim_get_current_buf()
vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
local notify_msg
local notify = vim.notify
vim.notify = function(msg, log_level)
notify_msg = msg
end
vim.lsp.buf.format({ name = 'does-not-exist' })
vim.notify = notify
return notify_msg
]] )
eq ( "[LSP] Format request failed, no matching language servers." , notify_msg )
client.stop ()
end ,
}
end )
it ( "Sends textDocument/formatting request to format buffer" , function ()
local expected_handlers = {
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
}
local client
test_rpc_server {
test_name = "basic_formatting" ,
on_init = function ( c )
client = c
end ,
on_handler = function ( _ , _ , ctx )
table.remove ( expected_handlers )
if ctx.method == "start" then
local notify_msg = exec_lua ( [[
local bufnr = vim.api.nvim_get_current_buf()
vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
local notify_msg
local notify = vim.notify
vim.notify = function(msg, log_level)
notify_msg = msg
end
vim.lsp.buf.format({ bufnr = bufnr })
vim.notify = notify
return notify_msg
]] )
eq ( NIL , notify_msg )
elseif ctx.method == "shutdown" then
client.stop ()
end
end ,
}
end )
2022-04-30 17:23:50 +02:00
it ( 'Can format async' , function ()
local expected_handlers = {
{ NIL , {}, { method = "shutdown" , client_id = 1 }};
{ NIL , {}, { method = "start" , client_id = 1 }};
}
local client
test_rpc_server {
test_name = "basic_formatting" ,
on_init = function ( c )
client = c
end ,
on_handler = function ( _ , _ , ctx )
table.remove ( expected_handlers )
if ctx.method == "start" then
local result = exec_lua ( [[
local bufnr = vim.api.nvim_get_current_buf()
vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
local notify_msg
local notify = vim.notify
vim.notify = function(msg, log_level)
notify_msg = msg
end
local handler = vim.lsp.handlers['textDocument/formatting']
local handler_called = false
vim.lsp.handlers['textDocument/formatting'] = function(...)
handler_called = true
end
vim.lsp.buf.format({ bufnr = bufnr, async = true })
vim.wait(1000, function() return handler_called end)
vim.notify = notify
vim.lsp.handlers['textDocument/formatting'] = handler
return {notify = notify_msg, handler_called = handler_called}
]] )
eq ({ handler_called = true }, result )
elseif ctx.method == "shutdown" then
client.stop ()
end
end ,
}
end )
2022-09-08 11:33:04 +02:00
it ( 'format formats range in visual mode' , function ()
2022-12-04 15:39:22 +01:00
exec_lua ( create_server_definition )
2022-09-08 11:33:04 +02:00
local result = exec_lua ( [[
2022-12-04 15:39:22 +01:00
local server = _create_server({ capabilities = {
documentFormattingProvider = true,
documentRangeFormattingProvider = true,
}})
2022-09-08 11:33:04 +02:00
local bufnr = vim.api.nvim_get_current_buf()
2022-12-04 15:39:22 +01:00
local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
2022-09-08 11:33:04 +02:00
vim.api.nvim_win_set_buf(0, bufnr)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, {'foo', 'bar'})
vim.api.nvim_win_set_cursor(0, { 1, 0 })
vim.cmd.normal('v')
vim.api.nvim_win_set_cursor(0, { 2, 3 })
vim.lsp.buf.format({ bufnr = bufnr, false })
2023-03-12 09:45:28 +01:00
vim.lsp.stop_client(client_id)
2022-12-04 15:39:22 +01:00
return server.messages
2022-09-08 11:33:04 +02:00
]] )
2022-12-08 10:55:01 +01:00
eq ( "textDocument/rangeFormatting" , result [ 3 ]. method )
2022-09-08 11:33:04 +02:00
local expected_range = {
start = { line = 0 , character = 0 },
[ 'end' ] = { line = 1 , character = 4 },
}
2022-12-08 10:55:01 +01:00
eq ( expected_range , result [ 3 ]. params.range )
2022-09-08 11:33:04 +02:00
end )
2023-03-12 09:45:28 +01:00
it ( 'format formats range in visual line mode' , function ()
exec_lua ( create_server_definition )
local result = exec_lua ( [[
local server = _create_server({ capabilities = {
documentFormattingProvider = true,
documentRangeFormattingProvider = true,
}})
local bufnr = vim.api.nvim_get_current_buf()
local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
vim.api.nvim_win_set_buf(0, bufnr)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, {'foo', 'bar baz'})
vim.api.nvim_win_set_cursor(0, { 1, 2 })
vim.cmd.normal('V')
vim.api.nvim_win_set_cursor(0, { 2, 1 })
vim.lsp.buf.format({ bufnr = bufnr, false })
-- Format again with visual lines going from bottom to top
-- Must result in same formatting
vim.cmd.normal("<ESC>")
vim.api.nvim_win_set_cursor(0, { 2, 1 })
vim.cmd.normal('V')
vim.api.nvim_win_set_cursor(0, { 1, 2 })
vim.lsp.buf.format({ bufnr = bufnr, false })
vim.lsp.stop_client(client_id)
return server.messages
]] )
local expected_methods = {
"initialize" ,
"initialized" ,
"textDocument/rangeFormatting" ,
"$/cancelRequest" ,
"textDocument/rangeFormatting" ,
"$/cancelRequest" ,
"shutdown" ,
"exit" ,
}
eq ( expected_methods , vim.tbl_map ( function ( x ) return x.method end , result ))
-- uses first column of start line and last column of end line
local expected_range = {
start = { line = 0 , character = 0 },
[ 'end' ] = { line = 1 , character = 7 },
}
eq ( expected_range , result [ 3 ]. params.range )
eq ( expected_range , result [ 5 ]. params.range )
end )
2023-01-24 06:15:43 +00:00
it ( 'Aborts with notify if no clients support requested method' , function ()
exec_lua ( create_server_definition )
exec_lua ( [[
vim.notify = function(msg, _)
notify_msg = msg
end
]] )
local fail_msg = "[LSP] Format request failed, no matching language servers."
local function check_notify ( name , formatting , range_formatting )
local timeout_msg = "[LSP][" .. name .. "] timeout"
exec_lua ( [[
local formatting, range_formatting, name = ...
local server = _create_server({ capabilities = {
documentFormattingProvider = formatting,
documentRangeFormattingProvider = range_formatting,
}})
vim.lsp.start({ name = name, cmd = server.cmd })
notify_msg = nil
vim.lsp.buf.format({ name = name, timeout_ms = 1 })
]] , formatting , range_formatting , name )
eq ( formatting and timeout_msg or fail_msg , exec_lua ( 'return notify_msg' ))
exec_lua ( [[
notify_msg = nil
vim.lsp.buf.format({ name = name, timeout_ms = 1, range = {start={1, 0}, ['end']={1, 0}}})
]] )
eq ( range_formatting and timeout_msg or fail_msg , exec_lua ( 'return notify_msg' ))
end
check_notify ( "none" , false , false )
check_notify ( "formatting" , true , false )
check_notify ( "rangeFormatting" , false , true )
check_notify ( "both" , true , true )
end )
2022-04-30 06:36:40 -07:00
end )
2022-08-24 20:25:34 +02:00
describe ( 'cmd' , function ()
it ( 'can connect to lsp server via rpc.connect' , function ()
local result = exec_lua [[
local uv = vim.loop
local server = uv.new_tcp()
local init = nil
server:bind('127.0.0.1', 0)
server:listen(127, function(err)
assert(not err, err)
local socket = uv.new_tcp()
server:accept(socket)
socket:read_start(require('vim.lsp.rpc').create_read_loop(function(body)
init = body
socket:close()
end))
end)
local port = server:getsockname().port
vim.lsp.start({ name = 'dummy', cmd = vim.lsp.rpc.connect('127.0.0.1', port) })
vim.wait(1000, function() return init ~= nil end)
assert(init, "server must receive `initialize` request")
server:close()
server:shutdown()
return vim.json.decode(init)
]]
eq ( result.method , "initialize" )
end )
end )
2022-09-20 09:28:23 +02:00
describe ( 'handlers' , function ()
it ( 'handler can return false as response' , function ()
local result = exec_lua [[
local uv = vim.loop
local server = uv.new_tcp()
local messages = {}
local responses = {}
server:bind('127.0.0.1', 0)
server:listen(127, function(err)
assert(not err, err)
local socket = uv.new_tcp()
server:accept(socket)
socket:read_start(require('vim.lsp.rpc').create_read_loop(function(body)
local payload = vim.json.decode(body)
if payload.method then
table.insert(messages, payload.method)
if payload.method == 'initialize' then
local msg = vim.json.encode({
id = payload.id,
jsonrpc = '2.0',
result = {
capabilities = {}
},
})
socket:write(table.concat({'Content-Length: ', tostring(#msg), '\r\n\r\n', msg}))
elseif payload.method == 'initialized' then
local msg = vim.json.encode({
id = 10,
jsonrpc = '2.0',
method = 'dummy',
params = {},
})
socket:write(table.concat({'Content-Length: ', tostring(#msg), '\r\n\r\n', msg}))
end
else
table.insert(responses, payload)
socket:close()
end
end))
end)
local port = server:getsockname().port
local handler_called = false
vim.lsp.handlers['dummy'] = function(err, result)
handler_called = true
return false
end
local client_id = vim.lsp.start({ name = 'dummy', cmd = vim.lsp.rpc.connect('127.0.0.1', port) })
local client = vim.lsp.get_client_by_id(client_id)
vim.wait(1000, function() return #messages == 2 and handler_called and #responses == 1 end)
server:close()
server:shutdown()
return {
messages = messages,
handler_called = handler_called,
responses = responses }
]]
local expected = {
messages = { 'initialize' , 'initialized' },
handler_called = true ,
responses = {
{
id = 10 ,
jsonrpc = '2.0' ,
result = false
}
}
}
eq ( expected , result )
end )
end )
2023-03-05 00:52:27 -06:00
describe ( 'vim.lsp._watchfiles' , function ()
it ( 'sends notifications when files change' , function ()
local root_dir = helpers.tmpname ()
os.remove ( root_dir )
2023-04-04 21:59:06 +02:00
mkdir ( root_dir )
2023-03-05 00:52:27 -06:00
exec_lua ( create_server_definition )
local result = exec_lua ( [[
local root_dir = ...
local server = _create_server()
local client_id = vim.lsp.start({
name = 'watchfiles-test',
cmd = server.cmd,
root_dir = root_dir,
2023-05-20 00:45:39 -05:00
capabilities = {
workspace = {
didChangeWatchedFiles = {
dynamicRegistration = true,
},
},
},
2023-03-05 00:52:27 -06:00
})
local expected_messages = 2 -- initialize, initialized
2023-03-26 04:41:27 -05:00
local watchfunc = require('vim.lsp._watchfiles')._watchfunc
local msg_wait_timeout = watchfunc == vim._watch.poll and 2500 or 200
2023-03-05 00:52:27 -06:00
local function wait_for_messages()
assert(vim.wait(msg_wait_timeout, function() return #server.messages == expected_messages end), 'Timed out waiting for expected number of messages. Current messages seen so far: ' .. vim.inspect(server.messages))
end
wait_for_messages()
vim.lsp.handlers['client/registerCapability'](nil, {
registrations = {
{
id = 'watchfiles-test-0',
method = 'workspace/didChangeWatchedFiles',
registerOptions = {
watchers = {
{
globPattern = '**/watch',
kind = 7,
},
},
},
},
},
}, { client_id = client_id })
2023-03-26 04:41:27 -05:00
if watchfunc == vim._watch.poll then
vim.wait(100)
end
2023-03-05 00:52:27 -06:00
local path = root_dir .. '/watch'
local file = io.open(path, 'w')
file:close()
expected_messages = expected_messages + 1
wait_for_messages()
os.remove(path)
expected_messages = expected_messages + 1
wait_for_messages()
return server.messages
]] , root_dir )
local function watched_uri ( fname )
return exec_lua ( [[
local root_dir, fname = ...
return vim.uri_from_fname(root_dir .. '/' .. fname)
]] , root_dir , fname )
end
eq ( 4 , # result )
eq ( 'workspace/didChangeWatchedFiles' , result [ 3 ]. method )
eq ({
changes = {
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Created]] ),
uri = watched_uri ( 'watch' ),
},
},
}, result [ 3 ]. params )
eq ( 'workspace/didChangeWatchedFiles' , result [ 4 ]. method )
eq ({
changes = {
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Deleted]] ),
uri = watched_uri ( 'watch' ),
},
},
}, result [ 4 ]. params )
end )
it ( 'correctly registers and unregisters' , function ()
2023-05-09 11:12:54 -05:00
local root_dir = '/some_dir'
2023-03-05 00:52:27 -06:00
exec_lua ( create_server_definition )
local result = exec_lua ( [[
local root_dir = ...
local server = _create_server()
local client_id = vim.lsp.start({
name = 'watchfiles-test',
cmd = server.cmd,
root_dir = root_dir,
2023-05-20 00:45:39 -05:00
capabilities = {
workspace = {
didChangeWatchedFiles = {
dynamicRegistration = true,
},
},
},
2023-03-05 00:52:27 -06:00
})
local expected_messages = 2 -- initialize, initialized
local function wait_for_messages()
assert(vim.wait(200, function() return #server.messages == expected_messages end), 'Timed out waiting for expected number of messages. Current messages seen so far: ' .. vim.inspect(server.messages))
end
wait_for_messages()
local send_event
require('vim.lsp._watchfiles')._watchfunc = function(_, _, callback)
2023-04-04 19:07:33 +02:00
local stopped = false
2023-03-05 00:52:27 -06:00
send_event = function(...)
2023-04-04 19:07:33 +02:00
if not stopped then
2023-03-05 00:52:27 -06:00
callback(...)
end
end
return function()
2023-04-04 19:07:33 +02:00
stopped = true
2023-03-05 00:52:27 -06:00
end
end
vim.lsp.handlers['client/registerCapability'](nil, {
registrations = {
{
id = 'watchfiles-test-0',
method = 'workspace/didChangeWatchedFiles',
registerOptions = {
watchers = {
{
globPattern = '**/*.watch0',
},
},
},
},
},
}, { client_id = client_id })
send_event(root_dir .. '/file.watch0', vim._watch.FileChangeType.Created)
send_event(root_dir .. '/file.watch1', vim._watch.FileChangeType.Created)
expected_messages = expected_messages + 1
wait_for_messages()
vim.lsp.handlers['client/registerCapability'](nil, {
registrations = {
{
id = 'watchfiles-test-1',
method = 'workspace/didChangeWatchedFiles',
registerOptions = {
watchers = {
{
globPattern = '**/*.watch1',
},
},
},
},
},
}, { client_id = client_id })
vim.lsp.handlers['client/unregisterCapability'](nil, {
unregisterations = {
{
id = 'watchfiles-test-0',
method = 'workspace/didChangeWatchedFiles',
},
},
}, { client_id = client_id })
send_event(root_dir .. '/file.watch0', vim._watch.FileChangeType.Created)
send_event(root_dir .. '/file.watch1', vim._watch.FileChangeType.Created)
expected_messages = expected_messages + 1
wait_for_messages()
return server.messages
]] , root_dir )
local function watched_uri ( fname )
return exec_lua ( [[
local root_dir, fname = ...
return vim.uri_from_fname(root_dir .. '/' .. fname)
]] , root_dir , fname )
end
eq ( 4 , # result )
eq ( 'workspace/didChangeWatchedFiles' , result [ 3 ]. method )
eq ({
changes = {
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Created]] ),
uri = watched_uri ( 'file.watch0' ),
},
},
}, result [ 3 ]. params )
eq ( 'workspace/didChangeWatchedFiles' , result [ 4 ]. method )
eq ({
changes = {
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Created]] ),
uri = watched_uri ( 'file.watch1' ),
},
},
}, result [ 4 ]. params )
end )
it ( 'correctly handles the registered watch kind' , function ()
local root_dir = 'some_dir'
exec_lua ( create_server_definition )
local result = exec_lua ( [[
local root_dir = ...
local server = _create_server()
local client_id = vim.lsp.start({
name = 'watchfiles-test',
cmd = server.cmd,
root_dir = root_dir,
2023-05-20 00:45:39 -05:00
capabilities = {
workspace = {
didChangeWatchedFiles = {
dynamicRegistration = true,
},
},
},
2023-03-05 00:52:27 -06:00
})
local expected_messages = 2 -- initialize, initialized
local function wait_for_messages()
assert(vim.wait(200, function() return #server.messages == expected_messages end), 'Timed out waiting for expected number of messages. Current messages seen so far: ' .. vim.inspect(server.messages))
end
wait_for_messages()
local watch_callbacks = {}
local function send_event(...)
for _, cb in ipairs(watch_callbacks) do
cb(...)
end
end
require('vim.lsp._watchfiles')._watchfunc = function(_, _, callback)
table.insert(watch_callbacks, callback)
return function()
-- noop because this test never stops the watch
end
end
local protocol = require('vim.lsp.protocol')
local watchers = {}
local max_kind = protocol.WatchKind.Create + protocol.WatchKind.Change + protocol.WatchKind.Delete
for i = 0, max_kind do
table.insert(watchers, {
globPattern = {
2023-05-09 11:12:54 -05:00
baseUri = vim.uri_from_fname('/dir'),
2023-03-05 00:52:27 -06:00
pattern = 'watch'..tostring(i),
},
kind = i,
})
end
vim.lsp.handlers['client/registerCapability'](nil, {
registrations = {
{
id = 'watchfiles-test-kind',
method = 'workspace/didChangeWatchedFiles',
registerOptions = {
watchers = watchers,
},
},
},
}, { client_id = client_id })
for i = 0, max_kind do
2023-05-09 11:12:54 -05:00
local filename = '/dir/watch' .. tostring(i)
2023-03-05 00:52:27 -06:00
send_event(filename, vim._watch.FileChangeType.Created)
send_event(filename, vim._watch.FileChangeType.Changed)
send_event(filename, vim._watch.FileChangeType.Deleted)
end
expected_messages = expected_messages + 1
wait_for_messages()
return server.messages
]] , root_dir )
local function watched_uri ( fname )
return exec_lua ( [[
2023-05-09 11:12:54 -05:00
local fname = ...
return vim.uri_from_fname('/dir/' .. fname)
2023-03-05 00:52:27 -06:00
]] , fname )
end
eq ( 3 , # result )
eq ( 'workspace/didChangeWatchedFiles' , result [ 3 ]. method )
eq ({
changes = {
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Created]] ),
uri = watched_uri ( 'watch1' ),
},
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Changed]] ),
uri = watched_uri ( 'watch2' ),
},
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Created]] ),
uri = watched_uri ( 'watch3' ),
},
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Changed]] ),
uri = watched_uri ( 'watch3' ),
},
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Deleted]] ),
uri = watched_uri ( 'watch4' ),
},
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Created]] ),
uri = watched_uri ( 'watch5' ),
},
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Deleted]] ),
uri = watched_uri ( 'watch5' ),
},
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Changed]] ),
uri = watched_uri ( 'watch6' ),
},
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Deleted]] ),
uri = watched_uri ( 'watch6' ),
},
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Created]] ),
uri = watched_uri ( 'watch7' ),
},
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Changed]] ),
uri = watched_uri ( 'watch7' ),
},
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Deleted]] ),
uri = watched_uri ( 'watch7' ),
},
},
}, result [ 3 ]. params )
end )
it ( 'prunes duplicate events' , function ()
local root_dir = 'some_dir'
exec_lua ( create_server_definition )
local result = exec_lua ( [[
local root_dir = ...
local server = _create_server()
local client_id = vim.lsp.start({
name = 'watchfiles-test',
cmd = server.cmd,
root_dir = root_dir,
2023-05-20 00:45:39 -05:00
capabilities = {
workspace = {
didChangeWatchedFiles = {
dynamicRegistration = true,
},
},
},
2023-03-05 00:52:27 -06:00
})
local expected_messages = 2 -- initialize, initialized
local function wait_for_messages()
assert(vim.wait(200, function() return #server.messages == expected_messages end), 'Timed out waiting for expected number of messages. Current messages seen so far: ' .. vim.inspect(server.messages))
end
wait_for_messages()
local send_event
require('vim.lsp._watchfiles')._watchfunc = function(_, _, callback)
send_event = callback
return function()
-- noop because this test never stops the watch
end
end
vim.lsp.handlers['client/registerCapability'](nil, {
registrations = {
{
id = 'watchfiles-test-kind',
method = 'workspace/didChangeWatchedFiles',
registerOptions = {
watchers = {
{
globPattern = '**/*',
},
},
},
},
},
}, { client_id = client_id })
send_event('file1', vim._watch.FileChangeType.Created)
send_event('file1', vim._watch.FileChangeType.Created) -- pruned
send_event('file1', vim._watch.FileChangeType.Changed)
send_event('file2', vim._watch.FileChangeType.Created)
send_event('file1', vim._watch.FileChangeType.Changed) -- pruned
expected_messages = expected_messages + 1
wait_for_messages()
return server.messages
]] , root_dir )
local function watched_uri ( fname )
return exec_lua ( [[
return vim.uri_from_fname(...)
]] , fname )
end
eq ( 3 , # result )
eq ( 'workspace/didChangeWatchedFiles' , result [ 3 ]. method )
eq ({
changes = {
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Created]] ),
uri = watched_uri ( 'file1' ),
},
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Changed]] ),
uri = watched_uri ( 'file1' ),
},
{
type = exec_lua ( [[return vim.lsp.protocol.FileChangeType.Created]] ),
uri = watched_uri ( 'file2' ),
},
},
}, result [ 3 ]. params )
end )
2023-05-20 00:45:39 -05:00
it ( "ignores registrations by servers when the client doesn't advertise support" , function ()
exec_lua ( create_server_definition )
local result = exec_lua ( [[
local server = _create_server()
local client_id = vim.lsp.start({
name = 'watchfiles-test',
cmd = server.cmd,
root_dir = 'some_dir',
capabilities = {
workspace = {
didChangeWatchedFiles = {
dynamicRegistration = false,
},
},
},
})
local watching = false
require('vim.lsp._watchfiles')._watchfunc = function(_, _, callback)
-- Since the registration is ignored, this should not execute and `watching` should stay false
watching = true
return function() end
end
vim.lsp.handlers['client/registerCapability'](nil, {
registrations = {
{
id = 'watchfiles-test-kind',
method = 'workspace/didChangeWatchedFiles',
registerOptions = {
watchers = {
{
globPattern = '**/*',
},
},
},
},
},
}, { client_id = client_id })
-- Ensure no errors occur when unregistering something that was never really registered.
vim.lsp.handlers['client/unregisterCapability'](nil, {
unregisterations = {
{
id = 'watchfiles-test-kind',
method = 'workspace/didChangeWatchedFiles',
},
},
}, { client_id = client_id })
return watching
]] )
eq ( false , result )
end )
2023-03-05 00:52:27 -06:00
end )
2020-04-20 18:35:54 +03:00
end )