Skip to content

Commit

Permalink
Merge #3450 from MariaSolOs/start
Browse files Browse the repository at this point in the history
refactor: use vim.lsp.start instead of vim.lsp.start_client
  • Loading branch information
justinmk authored Nov 24, 2024
2 parents ac936a6 + 2eccb41 commit dafd61d
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 121 deletions.
3 changes: 3 additions & 0 deletions doc/lspconfig.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ the following added keys:
- {single_file_support} (`bool`) (default: nil) Determines if a server is
started without a matching root directory. See |lspconfig-single-file-support|.

- {silent} (`bool`) (default: false) Whether to suppress error reporting if the
LSP server fails to start.

- {on_new_config} (`function(new_config, new_root_dir)`) Function executed
after a root directory is detected. This is used to modify the server
configuration (including `cmd` itself). Most commonly, this is used to
Expand Down
5 changes: 3 additions & 2 deletions lua/lspconfig/configs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ local configs = {}
--- @class lspconfig.Config : vim.lsp.ClientConfig
--- @field enabled? boolean
--- @field single_file_support? boolean
--- @field silent? boolean
--- @field filetypes? string[]
--- @field filetype? string
--- @field on_new_config? fun(new_config: lspconfig.Config?, new_root_dir: string)
Expand Down Expand Up @@ -105,7 +106,7 @@ function configs.__newindex(t, config_name, config_def)
api.nvim_create_autocmd(event_conf.event, {
pattern = event_conf.pattern or '*',
callback = function(opt)
M.manager:try_add(opt.buf)
M.manager:try_add(opt.buf, nil, config.silent)
end,
group = lsp_group,
desc = string.format(
Expand Down Expand Up @@ -176,7 +177,7 @@ function configs.__newindex(t, config_name, config_def)
return
end
local pseudo_root = #bufname == 0 and pwd or util.path.dirname(util.path.sanitize(bufname))
M.manager:add(pseudo_root, true, bufnr)
M.manager:add(pseudo_root, true, bufnr, config.silent)
end
end)
end
Expand Down
175 changes: 56 additions & 119 deletions lua/lspconfig/manager.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 = {}
Expand All @@ -42,74 +42,51 @@ 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
--- @param bufnr integer
--- @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)
--- @param silent boolean
function M:_start_client(bufnr, new_config, root_dir, single_file, silent)
-- do nothing if the client is not enabled
if new_config.enabled == false then
return
Expand All @@ -125,13 +102,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)
Expand All @@ -150,88 +128,46 @@ 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,
silent = silent,
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)
---@param silent boolean
function M:add(root_dir, single_file, bufnr, silent)
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, silent)
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
Expand All @@ -241,7 +177,8 @@ end
--- a new client if one doesn't already exist for `bufnr`.
--- @param bufnr integer
--- @param project_root? string
function M:try_add(bufnr, project_root)
--- @param silent boolean
function M:try_add(bufnr, project_root, silent)
bufnr = bufnr or api.nvim_get_current_buf()

if vim.bo[bufnr].buftype == 'nofile' then
Expand All @@ -258,7 +195,7 @@ function M:try_add(bufnr, project_root)
end

if project_root then
self:add(project_root, false, bufnr)
self:add(project_root, false, bufnr, silent)
return
end

Expand All @@ -281,10 +218,10 @@ function M:try_add(bufnr, project_root)
end

if root_dir then
self:add(root_dir, false, bufnr)
self:add(root_dir, false, bufnr, silent)
elseif self.config.single_file_support then
local pseudo_root = #bufname == 0 and pwd or util.path.dirname(buf_path)
self:add(pseudo_root, true, bufnr)
self:add(pseudo_root, true, bufnr, silent)
end
end)
end
Expand All @@ -297,7 +234,7 @@ function M:try_add_wrapper(bufnr, project_root)
local config = self.config
-- `config.filetypes = nil` means all filetypes are valid.
if not config.filetypes or vim.tbl_contains(config.filetypes, vim.bo[bufnr].filetype) then
self:try_add(bufnr, project_root)
self:try_add(bufnr, project_root, config.silent)
end
end

Expand Down

0 comments on commit dafd61d

Please sign in to comment.