aboutsummaryrefslogtreecommitdiffstats
path: root/lua
diff options
context:
space:
mode:
authorRaphael <glepnir@neovim.pro>2023-05-06 19:35:25 +0800
committerGitHub <noreply@github.com>2023-05-06 19:35:25 +0800
commitf01429048950195bcab6c430b430fc44b2d9e05a (patch)
tree61465e4615f38d1c5798060f513271478924db15 /lua
parentfix(remarkls): make root_dir more restrictive (#2405) (diff)
downloadnvim-lspconfig-f01429048950195bcab6c430b430fc44b2d9e05a.tar
nvim-lspconfig-f01429048950195bcab6c430b430fc44b2d9e05a.tar.gz
nvim-lspconfig-f01429048950195bcab6c430b430fc44b2d9e05a.tar.bz2
nvim-lspconfig-f01429048950195bcab6c430b430fc44b2d9e05a.tar.lz
nvim-lspconfig-f01429048950195bcab6c430b430fc44b2d9e05a.tar.xz
nvim-lspconfig-f01429048950195bcab6c430b430fc44b2d9e05a.tar.zst
nvim-lspconfig-f01429048950195bcab6c430b430fc44b2d9e05a.zip
feat: wrap get root dir in coroutine . (#2565)
Co-authored-by: ii14 <59243201+ii14@users.noreply.github.com>
Diffstat (limited to 'lua')
-rw-r--r--lua/lspconfig/configs.lua176
-rw-r--r--lua/lspconfig/server_configurations/rust_analyzer.lua68
-rw-r--r--lua/lspconfig/ui/lspinfo.lua59
3 files changed, 174 insertions, 129 deletions
diff --git a/lua/lspconfig/configs.lua b/lua/lspconfig/configs.lua
index 209a6157..b10bd75d 100644
--- a/lua/lspconfig/configs.lua
+++ b/lua/lspconfig/configs.lua
@@ -4,6 +4,25 @@ 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
+
+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)
+ end
+ end))
+end
+
function configs.__newindex(t, config_name, config_def)
validate {
name = { config_name, 's' },
@@ -72,17 +91,10 @@ function configs.__newindex(t, config_name, config_def)
end
if config.autostart == true then
- local event
- local pattern
- if config.filetypes then
- event = 'FileType'
- pattern = table.concat(config.filetypes, ',')
- else
- event = 'BufReadPost'
- pattern = '*'
- end
- api.nvim_create_autocmd(event, {
- pattern = pattern,
+ local event_conf = config.filetypes and { event = 'FileType', pattern = config.filetypes }
+ or { event = 'BufReadPost' }
+ api.nvim_create_autocmd(event_conf.event, {
+ pattern = event_conf.pattern or '*',
callback = function(opt)
M.manager.try_add(opt.buf)
end,
@@ -97,54 +109,59 @@ function configs.__newindex(t, config_name, config_def)
local get_root_dir = config.root_dir
function M.launch()
- local root_dir
- if get_root_dir then
- local bufnr = api.nvim_get_current_buf()
- 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
+ local bufnr = api.nvim_get_current_buf()
+ local bufname = api.nvim_buf_get_name(bufnr)
+ if (#bufname == 0 and not config.single_file_support) or (#bufname ~= 0 and not util.bufname_valid(bufname)) then
+ return
+ end
+
+ local pwd = uv.cwd()
+
+ async_run(function()
+ local root_dir
+ if get_root_dir then
+ root_dir = get_root_dir(util.path.sanitize(bufname), bufnr)
+ reenter()
+ if not api.nvim_buf_is_valid(bufnr) then
return
end
- root_dir = get_root_dir(util.path.sanitize(bufname), bufnr)
end
- end
- if root_dir then
- api.nvim_create_autocmd('BufReadPost', {
- pattern = fn.fnameescape(root_dir) .. '/*',
- callback = function()
- M.manager.try_add_wrapper()
- end,
- group = lsp_group,
- desc = string.format(
- 'Checks whether server %s should attach to a newly opened buffer inside workspace %q.',
- config.name,
- root_dir
- ),
- })
- for _, bufnr in ipairs(api.nvim_list_bufs()) do
- local bufname = api.nvim_buf_get_name(bufnr)
- if util.bufname_valid(bufname) then
- local buf_dir = util.path.sanitize(bufname)
- if buf_dir:sub(1, root_dir:len()) == root_dir then
- M.manager.try_add_wrapper(bufnr)
+ if root_dir then
+ api.nvim_create_autocmd('BufReadPost', {
+ pattern = fn.fnameescape(root_dir) .. '/*',
+ callback = function(arg)
+ M.manager.try_add_wrapper(arg.buf, root_dir)
+ end,
+ group = lsp_group,
+ desc = string.format(
+ 'Checks whether server %s should attach to a newly opened buffer inside workspace %q.',
+ config.name,
+ root_dir
+ ),
+ })
+
+ for _, buf in ipairs(api.nvim_list_bufs()) do
+ local buf_name = api.nvim_buf_get_name(buf)
+ 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)
+ end
end
end
+ elseif config.single_file_support then
+ -- This allows on_new_config to use the parent directory of the file
+ -- Effectively this is the root from lspconfig's perspective, as we use
+ -- this to attach additional files in the same parent folder to the same server.
+ -- We just no longer send rootDirectory or workspaceFolders during initialization.
+ if not api.nvim_buf_is_valid(bufnr) or (#bufname ~= 0 and not util.bufname_valid(bufname)) then
+ return
+ end
+ local pseudo_root = #bufname == 0 and pwd or util.path.dirname(util.path.sanitize(bufname))
+ M.manager.add(pseudo_root, true, bufnr)
end
- elseif config.single_file_support then
- -- This allows on_new_config to use the parent directory of the file
- -- Effectively this is the root from lspconfig's perspective, as we use
- -- this to attach additional files in the same parent folder to the same server.
- -- We just no longer send rootDirectory or workspaceFolders during initialization.
- local bufname = api.nvim_buf_get_name(0)
- if #bufname ~= 0 and not util.bufname_valid(bufname) then
- return
- end
- local pseudo_root = #bufname == 0 and uv.cwd() or util.path.dirname(util.path.sanitize(bufname))
- M.manager.add(pseudo_root, true, api.nvim_get_current_buf())
- end
+ end)
end
-- Used by :LspInfo
@@ -238,14 +255,13 @@ function configs.__newindex(t, config_name, config_def)
-- 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)
+ 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 root_dir
+ local pwd = uv.cwd()
local bufname = api.nvim_buf_get_name(bufnr)
if #bufname == 0 and not config.single_file_support then
@@ -255,35 +271,39 @@ function configs.__newindex(t, config_name, config_def)
return
end
end
- local buf_path = util.path.sanitize(bufname)
- if get_root_dir then
- root_dir = get_root_dir(buf_path, bufnr)
+ if project_root then
+ manager.add(project_root, false, bufnr)
+ return
end
- if root_dir then
- manager.add(root_dir, false, bufnr)
- elseif config.single_file_support then
- local pseudo_root = #bufname == 0 and uv.cwd() or util.path.dirname(buf_path)
- manager.add(pseudo_root, true, bufnr)
- end
- end
+ local buf_path = util.path.sanitize(bufname)
- -- 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)
- bufnr = bufnr or api.nvim_get_current_buf()
- local buf_filetype = api.nvim_buf_get_option(bufnr, 'filetype')
- if config.filetypes then
- for _, filetype in ipairs(config.filetypes) do
- if buf_filetype == filetype then
- manager.try_add(bufnr)
+ 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
- -- `config.filetypes = nil` means all filetypes are valid.
- else
- manager.try_add(bufnr)
+
+ 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
@@ -317,8 +337,6 @@ function configs.__newindex(t, config_name, config_def)
M.document_config = config_def
rawset(t, config_name, M)
-
- return M
end
return setmetatable({}, configs)
diff --git a/lua/lspconfig/server_configurations/rust_analyzer.lua b/lua/lspconfig/server_configurations/rust_analyzer.lua
index d5c0d940..38039b91 100644
--- a/lua/lspconfig/server_configurations/rust_analyzer.lua
+++ b/lua/lspconfig/server_configurations/rust_analyzer.lua
@@ -10,6 +10,38 @@ local function reload_workspace(bufnr)
end)
end
+local function get_workspace_dir(cmd)
+ local co = assert(coroutine.running())
+
+ local stdout = {}
+ local stderr = {}
+ local jobid = vim.fn.jobstart(cmd, {
+ on_stdout = function(_, data, _)
+ stdout[#stdout + 1] = table.concat(data, '\n')
+ 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 (%q) failed:\n%s'):format(table.concat(cmd, ' '), table.concat(stderr, '')),
+ vim.log.levels.WARN
+ )
+ return
+ end
+
+ coroutine.yield()
+ stdout = vim.json.decode(table.concat(stdout, ''))
+ return stdout and stdout['workspace_root'] or nil
+end
+
return {
default_config = {
cmd = { 'rust-analyzer' },
@@ -21,36 +53,14 @@ return {
cmd[#cmd + 1] = '--manifest-path'
cmd[#cmd + 1] = util.path.join(cargo_crate_dir, 'Cargo.toml')
end
- local cargo_metadata = ''
- local cargo_metadata_err = ''
- local cm = vim.fn.jobstart(cmd, {
- on_stdout = function(_, d, _)
- cargo_metadata = table.concat(d, '\n')
- end,
- on_stderr = function(_, d, _)
- cargo_metadata_err = table.concat(d, '\n')
- end,
- stdout_buffered = true,
- stderr_buffered = true,
- })
- if cm > 0 then
- cm = vim.fn.jobwait({ cm })[1]
- else
- cm = -1
- end
- local cargo_workspace_dir = nil
- if cm == 0 then
- cargo_workspace_dir = vim.json.decode(cargo_metadata)['workspace_root']
- if cargo_workspace_dir ~= nil then
- cargo_workspace_dir = util.path.sanitize(cargo_workspace_dir)
- end
- else
- vim.notify(
- string.format('[lspconfig] cmd (%q) failed:\n%s', table.concat(cmd, ' '), cargo_metadata_err),
- vim.log.levels.WARN
- )
+
+ local cargo_workspace_root = get_workspace_dir(cmd)
+
+ if cargo_workspace_root then
+ cargo_workspace_root = util.path.sanitize(cargo_workspace_root)
end
- return cargo_workspace_dir
+
+ return cargo_workspace_root
or cargo_crate_dir
or util.root_pattern 'rust-project.json'(fname)
or util.find_git_ancestor(fname)
diff --git a/lua/lspconfig/ui/lspinfo.lua b/lua/lspconfig/ui/lspinfo.lua
index 7bc71d9d..2008efd0 100644
--- a/lua/lspconfig/ui/lspinfo.lua
+++ b/lua/lspconfig/ui/lspinfo.lua
@@ -6,6 +6,7 @@ local error_messages = {
cmd_not_found = 'Unable to find executable. Please check your path and ensure the server is installed',
no_filetype_defined = 'No filetypes defined, Please define filetypes in setup()',
root_dir_not_found = 'Not found.',
+ async_root_dir_function = 'Asynchronous root_dir functions are not supported in :LspInfo',
}
local helptags = {
@@ -15,8 +16,8 @@ local helptags = {
local function trim_blankspace(cmd)
local trimmed_cmd = {}
- for _, str in pairs(cmd) do
- table.insert(trimmed_cmd, str:match '^%s*(.*)')
+ for _, str in ipairs(cmd) do
+ trimmed_cmd[#trimmed_cmd + 1] = str:match '^%s*(.*)'
end
return trimmed_cmd
end
@@ -64,9 +65,25 @@ local function make_config_info(config, bufnr)
local buffer_dir = api.nvim_buf_call(bufnr, function()
return vim.fn.expand '%:p:h'
end)
- local root_dir = config.get_root_dir and config.get_root_dir(buffer_dir)
- if root_dir then
- config_info.root_dir = root_dir
+
+ if config.get_root_dir then
+ local root_dir
+ local co = coroutine.create(function()
+ local status, err = pcall(function()
+ root_dir = config.get_root_dir(buffer_dir)
+ end)
+ if not status then
+ vim.notify(('[lspconfig] unhandled error: %s'):format(tostring(err), vim.log.levels.WARN))
+ end
+ end)
+ coroutine.resume(co)
+ if root_dir then
+ config_info.root_dir = root_dir
+ elseif coroutine.status(co) == 'suspended' then
+ config_info.root_dir = error_messages.async_root_dir_function
+ else
+ config_info.root_dir = error_messages.root_dir_not_found
+ end
else
config_info.root_dir = error_messages.root_dir_not_found
vim.list_extend(config_info.helptags, helptags[error_messages.root_dir_not_found])
@@ -118,7 +135,7 @@ local function make_client_info(client, fname)
end
if workspace_folders then
- for _, schema in pairs(workspace_folders) do
+ for _, schema in ipairs(workspace_folders) do
local matched = true
local root_dir = uv.fs_realpath(schema.name)
if fname:sub(1, root_dir:len()) ~= root_dir then
@@ -188,19 +205,19 @@ return function()
local buf_lines = {}
local buf_client_ids = {}
- for _, client in pairs(buf_clients) do
- table.insert(buf_client_ids, client.id)
+ for _, client in ipairs(buf_clients) do
+ buf_client_ids[#buf_client_ids + 1] = client.id
end
local other_active_clients = {}
- for _, client in pairs(clients) do
+ for _, client in ipairs(clients) do
if not vim.tbl_contains(buf_client_ids, client.id) then
- table.insert(other_active_clients, client)
+ other_active_clients[#other_active_clients + 1] = client
end
end
-- insert the tips at the top of window
- table.insert(buf_lines, 'Press q or <Esc> to close this window. Press <Tab> to view server doc.')
+ buf_lines[#buf_lines + 1] = 'Press q or <Esc> to close this window. Press <Tab> to view server doc.'
local header = {
'',
@@ -215,7 +232,7 @@ return function()
}
vim.list_extend(buf_lines, buffer_clients_header)
- for _, client in pairs(buf_clients) do
+ for _, client in ipairs(buf_clients) do
local client_info = make_client_info(client, fname)
vim.list_extend(buf_lines, client_info)
end
@@ -227,7 +244,7 @@ return function()
if not vim.tbl_isempty(other_active_clients) then
vim.list_extend(buf_lines, other_active_section_header)
end
- for _, client in pairs(other_active_clients) do
+ for _, client in ipairs(other_active_clients) do
local client_info = make_client_info(client, fname)
vim.list_extend(buf_lines, client_info)
end
@@ -242,7 +259,7 @@ return function()
if not vim.tbl_isempty(other_matching_configs) then
vim.list_extend(buf_lines, other_matching_configs_header)
- for _, config in pairs(other_matching_configs) do
+ for _, config in ipairs(other_matching_configs) do
vim.list_extend(buf_lines, make_config_info(config, original_bufnr))
end
end
@@ -310,22 +327,22 @@ return function()
end
local desc = vim.tbl_get(config, 'document_config', 'docs', 'description')
if desc then
- table.insert(lines, string.format('# %s', config.name))
- table.insert(lines, '')
+ lines[#lines + 1] = string.format('# %s', config.name)
+ lines[#lines + 1] = ''
vim.list_extend(lines, vim.split(desc, '\n'))
- table.insert(lines, '')
+ lines[#lines + 1] = ''
end
end
- table.insert(lines, 'Press <Tab> to close server info.')
- table.insert(lines, '')
+ lines[#lines + 1] = 'Press <Tab> to close server info.'
+ lines[#lines + 1] = ''
- for _, client in pairs(buf_clients) do
+ for _, client in ipairs(buf_clients) do
local config = require('lspconfig.configs')[client.name]
append_lines(config)
end
- for _, config in pairs(other_matching_configs) do
+ for _, config in ipairs(other_matching_configs) do
append_lines(config)
end