fix(lsp): check for configuration workspace folders when reusing clients

This commit is contained in:
Maria José Solano 2024-11-24 13:43:27 -08:00 committed by Lewis Russell
parent 9c278af7cc
commit c2bf09ddff
3 changed files with 40 additions and 31 deletions

View File

@ -872,7 +872,9 @@ start({config}, {opts}) *vim.lsp.start()*
(`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean`) (`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean`)
Predicate used to decide if a client should be re-used. Predicate used to decide if a client should be re-used.
Used on all running clients. The default implementation Used on all running clients. The default implementation
re-uses a client if name and root_dir matches. re-uses a client if it has the same name and if the given
workspace folders (or root_dir) are all included in the
client's workspace folders.
• {bufnr}? (`integer`) Buffer handle to attach to if • {bufnr}? (`integer`) Buffer handle to attach to if
starting or re-using a client (0 for current). starting or re-using a client (0 for current).
• {attach}? (`boolean`) Whether to attach the client to a • {attach}? (`boolean`) Whether to attach the client to a

View File

@ -114,6 +114,22 @@ function lsp._unsupported_method(method)
return msg return msg
end end
---@private
---@param workspace_folders string|lsp.WorkspaceFolder[]?
---@return lsp.WorkspaceFolder[]?
function lsp._get_workspace_folders(workspace_folders)
if type(workspace_folders) == 'table' then
return workspace_folders
elseif type(workspace_folders) == 'string' then
return {
{
uri = vim.uri_from_fname(workspace_folders),
name = workspace_folders,
},
}
end
end
local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'error' } local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'error' }
local format_line_ending = { local format_line_ending = {
@ -196,19 +212,24 @@ local function reuse_client_default(client, config)
return false return false
end end
if config.root_dir then local config_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir)
local root = vim.uri_from_fname(config.root_dir) or {}
for _, dir in ipairs(client.workspace_folders or {}) do local config_folders_included = 0
-- note: do not need to check client.root_dir since that should be client.workspace_folders[1]
if root == dir.uri then
return true
end
end
end
-- TODO(lewis6991): also check config.workspace_folders
if not next(config_folders) then
return false return false
end
for _, config_folder in ipairs(config_folders) do
for _, client_folder in ipairs(client.workspace_folders) do
if config_folder.uri == client_folder.uri then
config_folders_included = config_folders_included + 1
break
end
end
end
return config_folders_included == #config_folders
end end
--- Reset defaults set by `set_defaults`. --- Reset defaults set by `set_defaults`.
@ -311,9 +332,10 @@ end
--- @inlinedoc --- @inlinedoc
--- ---
--- Predicate used to decide if a client should be re-used. Used on all --- Predicate used to decide if a client should be re-used. Used on all
--- running clients. The default implementation re-uses a client if name and --- running clients. The default implementation re-uses a client if it has the
--- root_dir matches. --- same name and if the given workspace folders (or root_dir) are all included
--- @field reuse_client? (fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean) --- in the client's workspace folders.
--- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean
--- ---
--- Buffer handle to attach to if starting or re-using a client (0 for current). --- Buffer handle to attach to if starting or re-using a client (0 for current).
--- @field bufnr? integer --- @field bufnr? integer

View File

@ -365,21 +365,6 @@ local function get_name(id, config)
return tostring(id) return tostring(id)
end end
--- @param workspace_folders string|lsp.WorkspaceFolder[]?
--- @return lsp.WorkspaceFolder[]?
local function get_workspace_folders(workspace_folders)
if type(workspace_folders) == 'table' then
return workspace_folders
elseif type(workspace_folders) == 'string' then
return {
{
uri = vim.uri_from_fname(workspace_folders),
name = workspace_folders,
},
}
end
end
--- @generic T --- @generic T
--- @param x elem_or_list<T>? --- @param x elem_or_list<T>?
--- @return T[] --- @return T[]
@ -417,7 +402,7 @@ function Client.create(config)
flags = config.flags or {}, flags = config.flags or {},
get_language_id = config.get_language_id or default_get_language_id, get_language_id = config.get_language_id or default_get_language_id,
capabilities = config.capabilities or lsp.protocol.make_client_capabilities(), capabilities = config.capabilities or lsp.protocol.make_client_capabilities(),
workspace_folders = get_workspace_folders(config.workspace_folders or config.root_dir), workspace_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir),
root_dir = config.root_dir, root_dir = config.root_dir,
_before_init_cb = config.before_init, _before_init_cb = config.before_init,
_on_init_cbs = ensure_list(config.on_init), _on_init_cbs = ensure_list(config.on_init),
@ -1174,7 +1159,7 @@ function Client:_add_workspace_folder(dir)
end end
end end
local wf = assert(get_workspace_folders(dir)) local wf = assert(lsp._get_workspace_folders(dir))
self:notify(ms.workspace_didChangeWorkspaceFolders, { self:notify(ms.workspace_didChangeWorkspaceFolders, {
event = { added = wf, removed = {} }, event = { added = wf, removed = {} },
@ -1189,7 +1174,7 @@ end
--- Remove a directory to the workspace folders. --- Remove a directory to the workspace folders.
--- @param dir string? --- @param dir string?
function Client:_remove_workspace_folder(dir) function Client:_remove_workspace_folder(dir)
local wf = assert(get_workspace_folders(dir)) local wf = assert(lsp._get_workspace_folders(dir))
self:notify(ms.workspace_didChangeWorkspaceFolders, { self:notify(ms.workspace_didChangeWorkspaceFolders, {
event = { added = {}, removed = wf }, event = { added = {}, removed = wf },