From 204f08ea407e4b3b7ad272287e1821f47e52d4b3 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 22 Aug 2023 12:12:06 +0100 Subject: refactor: move manager to separate module - Move manager logic to own module - Move async logic to own module - Improve type annotations --- lua/lspconfig/util.lua | 191 ++----------------------------------------------- 1 file changed, 6 insertions(+), 185 deletions(-) (limited to 'lua/lspconfig/util.lua') diff --git a/lua/lspconfig/util.lua b/lua/lspconfig/util.lua index f4e2ab78..2233f7ac 100644 --- a/lua/lspconfig/util.lua +++ b/lua/lspconfig/util.lua @@ -137,6 +137,8 @@ M.path = (function() end end + --- @param path string + --- @return string? local function dirname(path) local strip_dir_pat = '/([^/]+)$' local strip_sep_pat = '/$' @@ -227,191 +229,6 @@ M.path = (function() } end)() -local function check_in_workspace(client, root_dir) - if not client.workspace_folders then - return false - end - - for _, dir in ipairs(client.workspace_folders) do - if (root_dir .. '/'):sub(1, #dir.name + 1) == dir.name .. '/' then - return true - end - end - - return false -end - --- Returns a function(root_dir), which, when called with a root_dir it hasn't --- seen before, will call make_config(root_dir) and start a new client. -function M.server_per_root_dir_manager(make_config) - -- a table store the root dir with clients in this dir - local clients = {} - local manager = {} - - function manager.add(root_dir, single_file, bufnr) - root_dir = M.path.sanitize(root_dir) - - local function get_client_from_cache(client_name) - if vim.tbl_count(clients) == 0 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 - - 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 and client.name == client_name then - return client - end - end - end - - local function attach_and_cache(root, client_id) - 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 - end - end - - local new_config = make_config(root_dir) - - local function register_workspace_folders(client) - local params = { - event = { - added = { { uri = vim.uri_from_fname(root_dir), name = root_dir } }, - removed = {}, - }, - } - client.rpc.notify('workspace/didChangeWorkspaceFolders', params) - if not client.workspace_folders then - client.workspace_folders = {} - end - client.workspace_folders[#client.workspace_folders + 1] = params.event.added[1] - attach_and_cache(root_dir, client.id) - end - - local function start_new_client() - -- do nothing if the client is not enabled - if new_config.enabled == false then - return - end - if not new_config.cmd then - vim.notify( - string.format( - '[lspconfig] cmd not defined for %q. Manually set cmd in the setup {} call according to server_configurations.md, see :help lspconfig-index.', - new_config.name - ), - vim.log.levels.ERROR - ) - return - end - new_config.on_exit = M.add_hook_before(new_config.on_exit, function() - 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) - - -- Launch the server in the root directory used internally by lspconfig, if otherwise unset - -- also check that the path exist - if not new_config.cmd_cwd and uv.fs_realpath(root_dir) then - new_config.cmd_cwd = root_dir - end - - -- Sending rootDirectory and workspaceFolders as null is not explicitly - -- codified in the spec. Certain servers crash if initialized with a NULL - -- root directory. - if single_file then - new_config.root_dir = nil - new_config.workspace_folders = nil - end - local client_id = lsp.start_client(new_config) - if not client_id then - return - end - attach_and_cache(root_dir, client_id) - end - - local function attach_or_spawn(client) - if check_in_workspace(client, root_dir) then - return attach_and_cache(root_dir, client.id) - end - - local supported = vim.tbl_get(client, 'server_capabilities', 'workspace', 'workspaceFolders', 'supported') - if supported then - return register_workspace_folders(client) - end - start_new_client() - end - - local attach_after_client_initialized = function(client) - local timer = vim.loop.new_timer() - timer:start( - 0, - 10, - vim.schedule_wrap(function() - if client.initialized and client.server_capabilities and not timer:is_closing() then - attach_or_spawn(client) - timer:stop() - timer:close() - end - end) - ) - end - - local client = get_client_from_cache(new_config.name) - - if not client then - return start_new_client() - end - - if 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 - attach_or_spawn(client) - else - attach_after_client_initialized(client) - end - end - - function manager.clients() - local res = {} - for _, client_ids in pairs(clients) do - for _, id in ipairs(client_ids) do - local client = lsp.get_client_by_id(id) - if client then - res[#res + 1] = client - end - end - end - return res - end - - return manager -end - function M.search_ancestors(startpath, func) validate { func = { func, 'f' } } if func(startpath) then @@ -447,6 +264,7 @@ function M.root_pattern(...) return M.search_ancestors(startpath, matcher) end end + function M.find_git_ancestor(startpath) return M.search_ancestors(startpath, function(path) -- Support git directories and git files (worktrees) @@ -455,6 +273,7 @@ function M.find_git_ancestor(startpath) end end) end + function M.find_mercurial_ancestor(startpath) return M.search_ancestors(startpath, function(path) -- Support Mercurial directories @@ -463,6 +282,7 @@ function M.find_mercurial_ancestor(startpath) end end) end + function M.find_node_modules_ancestor(startpath) return M.search_ancestors(startpath, function(path) if M.path.is_dir(M.path.join(path, 'node_modules')) then @@ -470,6 +290,7 @@ function M.find_node_modules_ancestor(startpath) end end) end + function M.find_package_json_ancestor(startpath) return M.search_ancestors(startpath, function(path) if M.path.is_file(M.path.join(path, 'package.json')) then -- cgit v1.2.3-70-g09d2 From 875d7e84d01f834f336a33a54673151819bea415 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 22 Aug 2023 12:23:55 +0100 Subject: refactor: move async_run_command() --- lua/lspconfig/async.lua | 36 ++++++++++++++++++++++ lua/lspconfig/server_configurations/gopls.lua | 3 +- .../server_configurations/rust_analyzer.lua | 3 +- lua/lspconfig/util.lua | 34 -------------------- 4 files changed, 40 insertions(+), 36 deletions(-) (limited to 'lua/lspconfig/util.lua') diff --git a/lua/lspconfig/async.lua b/lua/lspconfig/async.lua index 8a160c82..3421ccc5 100644 --- a/lua/lspconfig/async.lua +++ b/lua/lspconfig/async.lua @@ -9,6 +9,42 @@ function M.run(func) end)) end +--- @param cmd string|string[] +--- @return string[]? +function M.run_command(cmd) + local co = assert(coroutine.running()) + + local stdout = {} + local stderr = {} + local jobid = vim.fn.jobstart(cmd, { + on_stdout = function(_, data, _) + data = table.concat(data, '\n') + if #data > 0 then + stdout[#stdout + 1] = data + end + end, + on_stderr = function(_, data, _) + stderr[#stderr + 1] = table.concat(data, '\n') + end, + on_exit = function() + coroutine.resume(co) + end, + stdout_buffered = true, + stderr_buffered = true, + }) + + if jobid <= 0 then + vim.notify(('[lspconfig] cmd go failed:\n%s'):format(table.concat(stderr, '')), vim.log.levels.WARN) + return + end + + coroutine.yield() + if next(stdout) == nil then + return nil + end + return stdout and stdout or nil +end + function M.reenter() if vim.in_fast_event() then local co = assert(coroutine.running()) diff --git a/lua/lspconfig/server_configurations/gopls.lua b/lua/lspconfig/server_configurations/gopls.lua index 139f63d8..f27033b7 100644 --- a/lua/lspconfig/server_configurations/gopls.lua +++ b/lua/lspconfig/server_configurations/gopls.lua @@ -1,4 +1,5 @@ local util = require 'lspconfig.util' +local async = require 'lspconfig.async' local mod_cache = nil return { @@ -8,7 +9,7 @@ return { root_dir = function(fname) -- see: https://github.com/neovim/nvim-lspconfig/issues/804 if not mod_cache then - local result = util.async_run_command 'go env GOMODCACHE' + local result = async.run_command 'go env GOMODCACHE' if result and result[1] then mod_cache = vim.trim(result[1]) end diff --git a/lua/lspconfig/server_configurations/rust_analyzer.lua b/lua/lspconfig/server_configurations/rust_analyzer.lua index fc79046d..f7cc2524 100644 --- a/lua/lspconfig/server_configurations/rust_analyzer.lua +++ b/lua/lspconfig/server_configurations/rust_analyzer.lua @@ -1,4 +1,5 @@ local util = require 'lspconfig.util' +local async = require 'lspconfig.async' local function reload_workspace(bufnr) bufnr = util.validate_bufnr(bufnr) @@ -50,7 +51,7 @@ return { cmd[#cmd + 1] = util.path.join(cargo_crate_dir, 'Cargo.toml') end - local result = util.async_run_command(cmd) + local result = async.run_command(cmd) local cargo_workspace_root if result and result[1] then diff --git a/lua/lspconfig/util.lua b/lua/lspconfig/util.lua index 2233f7ac..98f9a323 100644 --- a/lua/lspconfig/util.lua +++ b/lua/lspconfig/util.lua @@ -401,38 +401,4 @@ function M.strip_archive_subpath(path) return path end -function M.async_run_command(cmd) - local co = assert(coroutine.running()) - - local stdout = {} - local stderr = {} - local jobid = vim.fn.jobstart(cmd, { - on_stdout = function(_, data, _) - data = table.concat(data, '\n') - if #data > 0 then - stdout[#stdout + 1] = data - end - end, - on_stderr = function(_, data, _) - stderr[#stderr + 1] = table.concat(data, '\n') - end, - on_exit = function() - coroutine.resume(co) - end, - stdout_buffered = true, - stderr_buffered = true, - }) - - if jobid <= 0 then - vim.notify(('[lspconfig] cmd go failed:\n%s'):format(table.concat(stderr, '')), vim.log.levels.WARN) - return - end - - coroutine.yield() - if next(stdout) == nil then - return nil - end - return stdout and stdout or nil -end - return M -- cgit v1.2.3-70-g09d2