diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 2654d7f14f..1607f3492c 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -872,7 +872,9 @@ start({config}, {opts}) *vim.lsp.start()* (`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean`) 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 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 starting or re-using a client (0 for current). • {attach}? (`boolean`) Whether to attach the client to a diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 4717d7995a..b67b2d6988 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -114,6 +114,22 @@ function lsp._unsupported_method(method) return msg 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 format_line_ending = { @@ -196,19 +212,24 @@ local function reuse_client_default(client, config) return false end - if config.root_dir then - local root = vim.uri_from_fname(config.root_dir) - for _, dir in ipairs(client.workspace_folders or {}) do - -- note: do not need to check client.root_dir since that should be client.workspace_folders[1] - if root == dir.uri then - return true + local config_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir) + or {} + local config_folders_included = 0 + + if not next(config_folders) then + 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 - -- TODO(lewis6991): also check config.workspace_folders - - return false + return config_folders_included == #config_folders end --- Reset defaults set by `set_defaults`. @@ -311,9 +332,10 @@ end --- @inlinedoc --- --- 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 ---- root_dir matches. ---- @field reuse_client? (fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean) +--- running clients. The default implementation 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. +--- @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). --- @field bufnr? integer diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index a83d75bf75..7eb023da39 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -365,21 +365,6 @@ local function get_name(id, config) return tostring(id) 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 --- @param x elem_or_list? --- @return T[] @@ -417,7 +402,7 @@ function Client.create(config) flags = config.flags or {}, get_language_id = config.get_language_id or default_get_language_id, 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, _before_init_cb = config.before_init, _on_init_cbs = ensure_list(config.on_init), @@ -1174,7 +1159,7 @@ function Client:_add_workspace_folder(dir) end end - local wf = assert(get_workspace_folders(dir)) + local wf = assert(lsp._get_workspace_folders(dir)) self:notify(ms.workspace_didChangeWorkspaceFolders, { event = { added = wf, removed = {} }, @@ -1189,7 +1174,7 @@ end --- Remove a directory to the workspace folders. --- @param dir string? 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, { event = { added = {}, removed = wf },