diff options
| author | Lewis Russell <lewis6991@gmail.com> | 2023-08-22 14:21:06 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-08-22 14:21:06 +0100 |
| commit | 93c6826b16217eaef568ca5c224ea5d0c12bbb82 (patch) | |
| tree | a07ad86c520b63ab7843a28bb48a6b786fe1c1f7 /lua | |
| parent | fix(rust_analyzer): use attached buffer client send request (#2738) (diff) | |
| parent | refactor: move async_run_command() (diff) | |
| download | nvim-lspconfig-93c6826b16217eaef568ca5c224ea5d0c12bbb82.tar nvim-lspconfig-93c6826b16217eaef568ca5c224ea5d0c12bbb82.tar.gz nvim-lspconfig-93c6826b16217eaef568ca5c224ea5d0c12bbb82.tar.bz2 nvim-lspconfig-93c6826b16217eaef568ca5c224ea5d0c12bbb82.tar.lz nvim-lspconfig-93c6826b16217eaef568ca5c224ea5d0c12bbb82.tar.xz nvim-lspconfig-93c6826b16217eaef568ca5c224ea5d0c12bbb82.tar.zst nvim-lspconfig-93c6826b16217eaef568ca5c224ea5d0c12bbb82.zip | |
Merge pull request #2775 from lewis6991/refactor/manager
refactor: move manager to separate module
Diffstat (limited to 'lua')
| -rw-r--r-- | lua/lspconfig/async.lua | 58 | ||||
| -rw-r--r-- | lua/lspconfig/configs.lua | 122 | ||||
| -rw-r--r-- | lua/lspconfig/manager.lua | 297 | ||||
| -rw-r--r-- | lua/lspconfig/server_configurations/gopls.lua | 3 | ||||
| -rw-r--r-- | lua/lspconfig/server_configurations/rust_analyzer.lua | 3 | ||||
| -rw-r--r-- | lua/lspconfig/util.lua | 225 |
6 files changed, 397 insertions, 311 deletions
diff --git a/lua/lspconfig/async.lua b/lua/lspconfig/async.lua new file mode 100644 index 00000000..3421ccc5 --- /dev/null +++ b/lua/lspconfig/async.lua @@ -0,0 +1,58 @@ +local M = {} + +function M.run(func) + coroutine.resume(coroutine.create(function() + local status, err = pcall(func) + if not status then + vim.notify(('[lspconfig] unhandled error: %s'):format(tostring(err)), vim.log.levels.WARN) + end + 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()) + vim.schedule(function() + coroutine.resume(co) + end) + coroutine.yield() + end +end + +return M diff --git a/lua/lspconfig/configs.lua b/lua/lspconfig/configs.lua index e74570f1..532befd1 100644 --- a/lua/lspconfig/configs.lua +++ b/lua/lspconfig/configs.lua @@ -1,26 +1,28 @@ local util = require 'lspconfig.util' +local async = require 'lspconfig.async' local api, validate, lsp, uv, fn = vim.api, vim.validate, vim.lsp, vim.loop, vim.fn local tbl_deep_extend = vim.tbl_deep_extend local configs = {} -local function reenter() - if vim.in_fast_event() then - local co = assert(coroutine.running()) - vim.schedule(function() - coroutine.resume(co) - end) - coroutine.yield() - end -end +--- @class lspconfig.Config : lsp.ClientConfig +--- @field enabled? boolean +--- @field single_file_support? boolean +--- @field filetypes? string[] +--- @field filetype? string +--- @field on_new_config? function +--- @field autostart? boolean +--- @field package _on_attach? fun(client: lsp.Client, bufnr: integer) -local function async_run(func) - coroutine.resume(coroutine.create(function() - local status, err = pcall(func) - if not status then - vim.notify(('[lspconfig] unhandled error: %s'):format(tostring(err)), vim.log.levels.WARN) +--- @param cmd any +local function sanitize_cmd(cmd) + if cmd and type(cmd) == 'table' and not vim.tbl_isempty(cmd) then + local original = cmd[1] + cmd[1] = vim.fn.exepath(cmd[1]) + if #cmd[1] == 0 then + cmd[1] = original end - end)) + end end function configs.__newindex(t, config_name, config_def) @@ -60,6 +62,7 @@ function configs.__newindex(t, config_name, config_def) -- Force this part. default_config.name = config_name + --- @param user_config lspconfig.Config function M.setup(user_config) local lsp_group = api.nvim_create_augroup('lspconfig', { clear = false }) @@ -86,13 +89,7 @@ function configs.__newindex(t, config_name, config_def) local config = tbl_deep_extend('keep', user_config, default_config) - if config.cmd and type(config.cmd) == 'table' and not vim.tbl_isempty(config.cmd) then - local original = config.cmd[1] - config.cmd[1] = vim.fn.exepath(config.cmd[1]) - if #config.cmd[1] == 0 then - config.cmd[1] = original - end - end + sanitize_cmd(config.cmd) if util.on_setup then pcall(util.on_setup, config, user_config) @@ -104,7 +101,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) end, group = lsp_group, desc = string.format( @@ -125,11 +122,11 @@ function configs.__newindex(t, config_name, config_def) local pwd = uv.cwd() - async_run(function() + async.run(function() local root_dir if get_root_dir then root_dir = get_root_dir(util.path.sanitize(bufname), bufnr) - reenter() + async.reenter() if not api.nvim_buf_is_valid(bufnr) then return end @@ -139,7 +136,7 @@ function configs.__newindex(t, config_name, config_def) api.nvim_create_autocmd('BufReadPost', { pattern = fn.fnameescape(root_dir) .. '/*', callback = function(arg) - M.manager.try_add_wrapper(arg.buf, root_dir) + M.manager:try_add_wrapper(arg.buf, root_dir) end, group = lsp_group, desc = string.format( @@ -154,7 +151,7 @@ function configs.__newindex(t, config_name, config_def) if util.bufname_valid(buf_name) then local buf_dir = util.path.sanitize(buf_name) if buf_dir:sub(1, root_dir:len()) == root_dir then - M.manager.try_add_wrapper(buf, root_dir) + M.manager:try_add_wrapper(buf, root_dir) end end end @@ -167,7 +164,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) end end) end @@ -182,7 +179,7 @@ function configs.__newindex(t, config_name, config_def) -- In the case of a reload, close existing things. local reload = false if M.manager then - for _, client in ipairs(M.manager.clients()) do + for _, client in ipairs(M.manager:clients()) do client.stop(true) end reload = true @@ -190,7 +187,7 @@ function configs.__newindex(t, config_name, config_def) end local make_config = function(root_dir) - local new_config = tbl_deep_extend('keep', vim.empty_dict(), config) + local new_config = tbl_deep_extend('keep', vim.empty_dict(), config) --[[@as lspconfig.Config]] new_config.capabilities = tbl_deep_extend('keep', new_config.capabilities, { workspace = { configuration = true, @@ -257,69 +254,13 @@ function configs.__newindex(t, config_name, config_def) return new_config end - local manager = util.server_per_root_dir_manager(function(root_dir) - return make_config(root_dir) - end) - - -- Try to attach the buffer `bufnr` to a client using this config, creating - -- a new client if one doesn't already exist for `bufnr`. - function manager.try_add(bufnr, project_root) - bufnr = bufnr or api.nvim_get_current_buf() - - if api.nvim_buf_get_option(bufnr, 'buftype') == 'nofile' then - return - end - local pwd = uv.cwd() - - local bufname = api.nvim_buf_get_name(bufnr) - if #bufname == 0 and not config.single_file_support then - return - elseif #bufname ~= 0 then - if not util.bufname_valid(bufname) then - return - end - end - - if project_root then - manager.add(project_root, false, bufnr) - return - end - - local buf_path = util.path.sanitize(bufname) - - async_run(function() - local root_dir - if get_root_dir then - root_dir = get_root_dir(buf_path, bufnr) - reenter() - if not api.nvim_buf_is_valid(bufnr) then - return - end - end - - if root_dir then - manager.add(root_dir, false, bufnr) - elseif config.single_file_support then - local pseudo_root = #bufname == 0 and pwd or util.path.dirname(buf_path) - manager.add(pseudo_root, true, bufnr) - end - end) - end - - -- Check that the buffer `bufnr` has a valid filetype according to - -- `config.filetypes`, then do `manager.try_add(bufnr)`. - function manager.try_add_wrapper(bufnr, project_root) - -- `config.filetypes = nil` means all filetypes are valid. - if not config.filetypes or vim.tbl_contains(config.filetypes, vim.bo[bufnr].filetype) then - manager.try_add(bufnr, project_root) - end - end + local manager = require('lspconfig.manager').new(config, make_config) M.manager = manager M.make_config = make_config if reload and config.autostart ~= false then for _, bufnr in ipairs(api.nvim_list_bufs()) do - manager.try_add_wrapper(bufnr) + manager:try_add_wrapper(bufnr) end end end @@ -329,8 +270,9 @@ function configs.__newindex(t, config_name, config_def) if not client then return end - if client.config._on_attach then - client.config._on_attach(client, bufnr) + local config = client.config --[[@as lspconfig.Config]] + if config._on_attach then + config._on_attach(client, bufnr) end if client.config.commands and not vim.tbl_isempty(client.config.commands) then M.commands = vim.tbl_deep_extend('force', M.commands, client.config.commands) diff --git a/lua/lspconfig/manager.lua b/lua/lspconfig/manager.lua new file mode 100644 index 00000000..5d260b41 --- /dev/null +++ b/lua/lspconfig/manager.lua @@ -0,0 +1,297 @@ +local api = vim.api +local lsp = vim.lsp +local uv = vim.loop + +local async = require 'lspconfig.async' +local util = require 'lspconfig.util' + +---@param client lsp.Client +---@param root_dir string +---@return boolean +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 + +--- @class lspconfig.Manager +--- @field _clients table<string,table> +--- @field config lspconfig.Config +--- @field make_config fun(root_dir: string): lspconfig.Config +local M = {} + +--- @param config lspconfig.Config +--- @param make_config fun(root_dir: string): lspconfig.Config +--- @return lspconfig.Manager +function M.new(config, make_config) + return setmetatable({ + _clients = {}, + config = config, + make_config = make_config, + }, { + __index = M, + }) +end + +--- @private +function M:_get_client_from_cache(root_dir, client_name) + local clients = self._clients + 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 + +--- @private +--- @param bufnr integer +--- @param root string +--- @param client_id integer +function M:_attach_and_cache(bufnr, root, client_id) + 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 + end +end + +--- @private +--- @param bufnr integer +--- @param root_dir string +--- @param client lsp.Client +function M:_register_workspace_folders(bufnr, root_dir, 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] + 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) + -- 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 + + local clients = self._clients + + 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) + 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 + self:_attach_and_cache(bufnr, root_dir, client_id) +end + +--- @private +--- @param bufnr integer +--- @param new_config lspconfig.Config +--- @param root_dir string +--- @param client 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 + + local supported = vim.tbl_get(client, 'server_capabilities', 'workspace', 'workspaceFolders', 'supported') + if supported then + return self:_register_workspace_folders(bufnr, root_dir, client) + 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 lsp.Client +--- @param single_file boolean +function M:_attach_after_client_initialized(bufnr, new_config, root_dir, client, single_file) + local timer = assert(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 + 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 = self:_get_client_from_cache(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 +end + +--- @return 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) + end + end + return res +end + +--- Try to attach the buffer `bufnr` to a client using this config, creating +--- 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) + bufnr = bufnr or api.nvim_get_current_buf() + + if vim.bo[bufnr].buftype == 'nofile' then + return + end + + local bufname = api.nvim_buf_get_name(bufnr) + if #bufname == 0 and not self.config.single_file_support then + return + end + + if #bufname ~= 0 and not util.bufname_valid(bufname) then + return + end + + if project_root then + self:add(project_root, false, bufnr) + return + end + + local buf_path = util.path.sanitize(bufname) + + local get_root_dir = self.config.root_dir + + local pwd = assert(uv.cwd()) + + async.run(function() + local root_dir + if get_root_dir then + root_dir = get_root_dir(buf_path, bufnr) + async.reenter() + if not api.nvim_buf_is_valid(bufnr) then + return + end + end + + if root_dir then + self:add(root_dir, false, bufnr) + 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) + end + end) +end + +--- Check that the buffer `bufnr` has a valid filetype according to +--- `config.filetypes`, then do `manager.try_add(bufnr)`. +--- @param bufnr integer +--- @param project_root? string +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) + end +end + +return M 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 cdabbb39..6ed471d2 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) @@ -54,7 +55,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 f4e2ab78..98f9a323 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 @@ -580,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 |
