aboutsummaryrefslogtreecommitdiffstats
path: root/lua
diff options
context:
space:
mode:
authorRaphael <glepnir@neovim.pro>2023-01-27 15:57:39 +0800
committerGitHub <noreply@github.com>2023-01-27 15:57:39 +0800
commit01185a87523b7054038dd60f0524ea3a8e1c6885 (patch)
treee11307b3cd1ddf9577cd5a8285b24d8a4960bc11 /lua
parentdocs: update server_configurations.md (diff)
downloadnvim-lspconfig-01185a87523b7054038dd60f0524ea3a8e1c6885.tar
nvim-lspconfig-01185a87523b7054038dd60f0524ea3a8e1c6885.tar.gz
nvim-lspconfig-01185a87523b7054038dd60f0524ea3a8e1c6885.tar.bz2
nvim-lspconfig-01185a87523b7054038dd60f0524ea3a8e1c6885.tar.lz
nvim-lspconfig-01185a87523b7054038dd60f0524ea3a8e1c6885.tar.xz
nvim-lspconfig-01185a87523b7054038dd60f0524ea3a8e1c6885.tar.zst
nvim-lspconfig-01185a87523b7054038dd60f0524ea3a8e1c6885.zip
fix: check server support workspacefolders (#2418)
add server workspaceFolder check, if server not have workspacefolders.supported field will spawn a new server instance. https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_workspaceFolders
Diffstat (limited to 'lua')
-rw-r--r--lua/lspconfig/util.lua179
1 files changed, 100 insertions, 79 deletions
diff --git a/lua/lspconfig/util.lua b/lua/lspconfig/util.lua
index 843e63cb..86483731 100644
--- a/lua/lspconfig/util.lua
+++ b/lua/lspconfig/util.lua
@@ -237,103 +237,64 @@ function M.server_per_root_dir_manager(make_config)
function manager.add(root_dir, single_file, bufnr)
root_dir = M.path.sanitize(root_dir)
- local client_id_iterator = function(client_ids, conf)
- for _, id in ipairs(client_ids) do
- local client = lsp.get_client_by_id(id)
- if client and client.name == conf.name then
- return client
- end
- end
- end
-
- local register_to_clients = function(id)
- if not clients[root_dir] then
- clients[root_dir] = {}
- end
- if not vim.tbl_contains(clients[root_dir], id) then
- table.insert(clients[root_dir], id)
- end
- end
-
- -- get client which support workspace from clients table
- local get_client_from_cache = function(conf)
+ local function get_client_from_cache(client_name)
if vim.tbl_count(clients) == 0 then
return
end
- local client
if clients[root_dir] then
- client = client_id_iterator(clients[root_dir], conf)
- else
- for _, ids in pairs(clients) do
- client = client_id_iterator(ids, conf)
- if client then
- break
+ for _, id in pairs(clients[root_dir]) do
+ local client = lsp.get_client_by_id(id)
+ if client.name == client_name then
+ return client
end
end
end
- return client
+ local all_client_ids = {}
+ vim.tbl_map(function(val)
+ vim.list_extend(all_client_ids, { unpack(val) })
+ end, clients)
+
+ for _, id in ipairs(all_client_ids) do
+ local client = lsp.get_client_by_id(id)
+ if client.name == client_name then
+ return client
+ end
+ end
+ end
+
+ local function register_to_clients(root, id)
+ if not clients[root] then
+ clients[root] = {}
+ end
+ if not vim.tbl_contains(clients[root], id) then
+ table.insert(clients[root], id)
+ end
end
local new_config = make_config(root_dir)
- local client = get_client_from_cache(new_config)
- --TODO(glepnir): do we need check language server support the workspaceFOlders?
- --some server support it but the it not show in server_capabilities
- local register_workspace_folders = function(client_instance)
+ local function register_workspace_folders(client)
local params = {
event = {
added = { { uri = vim.uri_from_fname(root_dir), name = root_dir } },
removed = {},
},
}
- for _, schema in ipairs(client_instance.workspace_folders or {}) do
+ for _, schema in ipairs(client.workspace_folders or {}) do
if schema.name == root_dir then
return
end
end
- client_instance.rpc.notify('workspace/didChangeWorkspaceFolders', params)
- if not client_instance.workspace_folders then
- client_instance.workspace_folders = {}
- end
- table.insert(client_instance.workspace_folders, params.event.added[1])
- end
-
- local attach_after_client_initialized = function(client_instance)
- local timer = vim.loop.new_timer()
- timer:start(
- 0,
- 10,
- vim.schedule_wrap(function()
- if client_instance.initialized and not timer:is_closing() then
- lsp.buf_attach_client(bufnr, client_instance.id)
- if not single_file then
- register_workspace_folders(client_instance)
- end
- register_to_clients(client_instance.id)
- timer:stop()
- timer:close()
- end
- end)
- )
- end
-
- if client then
- if client.initialized then
- lsp.buf_attach_client(bufnr, client.id)
- if not single_file then
- register_workspace_folders(client)
- end
- register_to_clients(client.id)
- else
- attach_after_client_initialized(client)
+ client.rpc.notify('workspace/didChangeWorkspaceFolders', params)
+ if not client.workspace_folders then
+ client.workspace_folders = {}
end
- return
+ table.insert(client.workspace_folders, params.event.added[1])
end
- local client_id
- local start_new_client = function()
+ local function start_new_client()
-- do nothing if the client is not enabled
if new_config.enabled == false then
return
@@ -349,9 +310,10 @@ function M.server_per_root_dir_manager(make_config)
return
end
new_config.on_exit = M.add_hook_before(new_config.on_exit, function()
- for k, v in pairs(clients[root_dir]) do
- if v == client_id then
- table.remove(clients[root_dir], k)
+ for index, id in pairs(clients[root_dir]) do
+ local exist = lsp.get_client_by_id(id)
+ if exist.name == new_config.name then
+ table.remove(clients[root_dir], index)
end
end
end)
@@ -369,19 +331,78 @@ function M.server_per_root_dir_manager(make_config)
new_config.root_dir = nil
new_config.workspace_folders = nil
end
- client_id = lsp.start_client(new_config)
+ return lsp.start_client(new_config)
+ end
+
+ local function attach_or_spawn(client)
+ local supported = vim.tbl_get(client, 'server_capabilities', 'workspace', 'workspaceFolders', 'supported')
+ local client_id
+ if supported then
+ lsp.buf_attach_client(bufnr, client.id)
+ register_workspace_folders(client)
+ client_id = client.id
+ else
+ client_id = start_new_client()
+ if not client_id then
+ return
+ end
+ lsp.buf_attach_client(bufnr, client_id)
+ end
+ register_to_clients(root_dir, client_id)
end
+ -- this function used for some situation like load from session
+ -- eg:
+ -- session have two same filetype in two project. file A trigger Filetyp event
+ -- start server. file B also trigger Filetype event. we start server is async.
+ -- that mean when file A spawn new server this server maybe not initialized and
+ -- don't exchanged server_capabilities so the File B need wait the server initialized
+ -- if server support workspaceFolders then regisert File B root into workspaceFolders
+ -- otherwise spawn a new one.
+ local attach_after_client_initialized = function(client_instance)
+ local timer = vim.loop.new_timer()
+ timer:start(
+ 0,
+ 10,
+ vim.schedule_wrap(function()
+ if client_instance.initialized and client_instance.server_capabilities and not timer:is_closing() then
+ attach_or_spawn(client_instance)
+ timer:stop()
+ timer:close()
+ end
+ end)
+ )
+ end
+
+ local client = get_client_from_cache(new_config.name)
+
if not client then
- start_new_client()
+ local client_id = start_new_client()
+ if not client_id then
+ return
+ end
+ lsp.buf_attach_client(bufnr, client_id)
+ register_to_clients(root_dir, client_id)
+ return
+ end
+
+ if clients[root_dir] then
+ lsp.buf_attach_client(bufnr, client.id)
+ return
end
- if not client_id then
+ if single_file then
+ lsp.buf_attach_client(bufnr, client.id)
return
end
- lsp.buf_attach_client(bufnr, client_id)
- register_to_clients(client_id)
+ -- make sure neovim had exchanged capabilities from language server
+ -- it's useful to check server support workspaceFolders or not
+ if client.initialized and client.server_capabilities then
+ attach_or_spawn(client)
+ else
+ attach_after_client_initialized(client)
+ end
end
function manager.clients()