aboutsummaryrefslogtreecommitdiffstats
path: root/lua/lspconfig/manager.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua/lspconfig/manager.lua')
-rw-r--r--lua/lspconfig/manager.lua160
1 files changed, 46 insertions, 114 deletions
diff --git a/lua/lspconfig/manager.lua b/lua/lspconfig/manager.lua
index 7d6fb88c..5a0c635c 100644
--- a/lua/lspconfig/manager.lua
+++ b/lua/lspconfig/manager.lua
@@ -8,7 +8,7 @@ local util = require 'lspconfig.util'
---@param client vim.lsp.Client
---@param root_dir string
---@return boolean
-local function check_in_workspace(client, root_dir)
+local function is_dir_in_workspace_folders(client, root_dir)
if not client.workspace_folders then
return false
end
@@ -23,7 +23,7 @@ local function check_in_workspace(client, root_dir)
end
--- @class lspconfig.Manager
---- @field _clients table<string,integer[]>
+--- @field _clients table<string,table<string, vim.lsp.Client>> root dir -> (client name -> client)
--- @field config lspconfig.Config
--- @field make_config fun(root_dir: string): lspconfig.Config
local M = {}
@@ -42,66 +42,42 @@ function M.new(config, make_config)
end
--- @private
---- @param clients table<string,integer[]>
---- @param root_dir string
---- @param client_name string
---- @return vim.lsp.Client?
-local function get_client(clients, root_dir, client_name)
- if vim.tbl_isempty(clients) then
- return
- end
-
- if clients[root_dir] then
- for _, id in pairs(clients[root_dir]) do
- local client = lsp.get_client_by_id(id)
- if client and client.name == client_name then
- return client
- end
- end
- end
-
- for _, ids in pairs(clients) do
- for _, id in ipairs(ids) do
- local client = lsp.get_client_by_id(id)
- if client and client.name == client_name then
- return client
- end
- end
- end
-end
-
---- @private
---- @param bufnr integer
--- @param root string
---- @param client_id integer
-function M:_attach_and_cache(bufnr, root, client_id)
+--- @param client vim.lsp.Client
+function M:_cache_client(root, client)
local clients = self._clients
- lsp.buf_attach_client(bufnr, client_id)
if not clients[root] then
clients[root] = {}
end
- if not vim.tbl_contains(clients[root], client_id) then
- clients[root][#clients[root] + 1] = client_id
+ if not clients[root][client.name] then
+ clients[root][client.name] = client
end
end
--- @private
---- @param bufnr integer
--- @param root_dir string
--- @param client vim.lsp.Client
-function M:_register_workspace_folders(bufnr, root_dir, client)
+function M:_notify_workspace_folder_added(root_dir, client)
+ if is_dir_in_workspace_folders(client, root_dir) then
+ return
+ end
+
+ local supported = vim.tbl_get(client, 'server_capabilities', 'workspace', 'workspaceFolders', 'supported')
+ if not supported then
+ return
+ end
+
local params = {
event = {
added = { { uri = vim.uri_from_fname(root_dir), name = root_dir } },
removed = {},
},
}
- client.rpc.notify('workspace/didChangeWorkspaceFolders', params)
+ client.rpc.notify(lsp.protocol.Methods.workspace_didChangeWorkspaceFolders, params)
if not client.workspace_folders then
client.workspace_folders = {}
end
client.workspace_folders[#client.workspace_folders + 1] = params.event.added[1]
- self:_attach_and_cache(bufnr, root_dir, client.id)
end
--- @private
@@ -109,7 +85,7 @@ end
--- @param new_config lspconfig.Config
--- @param root_dir string
--- @param single_file boolean
-function M:_start_new_client(bufnr, new_config, root_dir, single_file)
+function M:_start_client(bufnr, new_config, root_dir, single_file)
-- do nothing if the client is not enabled
if new_config.enabled == false then
return
@@ -125,13 +101,14 @@ function M:_start_new_client(bufnr, new_config, root_dir, single_file)
return
end
- local clients = self._clients
+ new_config.on_init = util.add_hook_before(new_config.on_init, function(client)
+ self:_notify_workspace_folder_added(root_dir, client)
+ end)
new_config.on_exit = util.add_hook_before(new_config.on_exit, function()
- for index, id in pairs(clients[root_dir]) do
- local exist = assert(lsp.get_client_by_id(id))
- if exist.name == new_config.name then
- table.remove(clients[root_dir], index)
+ for name in pairs(self._clients[root_dir]) do
+ if name == new_config.name then
+ self._clients[root_dir][name] = nil
end
end
end)
@@ -150,88 +127,43 @@ function M:_start_new_client(bufnr, new_config, root_dir, single_file)
new_config.workspace_folders = nil
end
- -- TODO: Replace lsp.start_client with lsp.start
- local client_id, err = lsp.start_client(new_config)
- if not client_id then
- if err then
- vim.notify(err, vim.log.levels.WARN)
- end
- return
- end
- self:_attach_and_cache(bufnr, root_dir, client_id)
-end
+ local client_id = lsp.start(new_config, {
+ bufnr = bufnr,
+ reuse_client = function(existing_client)
+ if (self._clients[root_dir] or {})[existing_client.name] then
+ self:_notify_workspace_folder_added(root_dir, existing_client)
+ return true
+ end
---- @private
---- @param bufnr integer
---- @param new_config lspconfig.Config
---- @param root_dir string
---- @param client vim.lsp.Client
---- @param single_file boolean
-function M:_attach_or_spawn(bufnr, new_config, root_dir, client, single_file)
- if check_in_workspace(client, root_dir) then
- return self:_attach_and_cache(bufnr, root_dir, client.id)
- end
+ for _, dir_clients in pairs(self._clients) do
+ if dir_clients[existing_client.name] then
+ self:_notify_workspace_folder_added(root_dir, existing_client)
+ return true
+ end
+ end
- local supported = vim.tbl_get(client, 'server_capabilities', 'workspace', 'workspaceFolders', 'supported')
- if supported then
- return self:_register_workspace_folders(bufnr, root_dir, client)
+ return false
+ end,
+ })
+ if client_id then
+ self:_cache_client(root_dir, assert(lsp.get_client_by_id(client_id)))
end
- self:_start_new_client(bufnr, new_config, root_dir, single_file)
-end
-
---- @private
---- @param bufnr integer
---- @param new_config lspconfig.Config
---- @param root_dir string
---- @param client vim.lsp.Client
---- @param single_file boolean
-function M:_attach_after_client_initialized(bufnr, new_config, root_dir, client, single_file)
- local timer = assert(uv.new_timer())
- timer:start(
- 0,
- 10,
- vim.schedule_wrap(function()
- if client.initialized and client.server_capabilities and not timer:is_closing() then
- self:_attach_or_spawn(bufnr, new_config, root_dir, client, single_file)
- timer:stop()
- timer:close()
- end
- end)
- )
end
---@param root_dir string
---@param single_file boolean
---@param bufnr integer
-function M:add(root_dir, single_file, bufnr)
root_dir = util.path.sanitize(root_dir)
local new_config = self.make_config(root_dir)
- local client = get_client(self._clients, root_dir, new_config.name)
-
- if not client then
- return self:_start_new_client(bufnr, new_config, root_dir, single_file)
- end
-
- if self._clients[root_dir] or single_file then
- lsp.buf_attach_client(bufnr, client.id)
- return
- end
-
- -- 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
- self:_attach_or_spawn(bufnr, new_config, root_dir, client, single_file)
- else
- self:_attach_after_client_initialized(bufnr, new_config, root_dir, client, single_file)
- end
+ self:_start_client(bufnr, new_config, root_dir, single_file)
end
--- @return vim.lsp.Client[]
function M:clients()
local res = {}
- for _, client_ids in pairs(self._clients) do
- for _, id in ipairs(client_ids) do
- res[#res + 1] = lsp.get_client_by_id(id)
+ for _, dir_clients in pairs(self._clients) do
+ for _, client in pairs(dir_clients) do
+ res[#res + 1] = client
end
end
return res