Skip to content

Commit 7c66c1b

Browse files
Marc Jakobimrcjkb
authored andcommitted
perf(lsp): default to async root directory detection
Supersedes #695 Thanks @saghen!
1 parent 1fcb991 commit 7c66c1b

File tree

4 files changed

+209
-180
lines changed

4 files changed

+209
-180
lines changed

lua/rustaceanvim/cargo.lua

Lines changed: 89 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,84 @@ local os = require('rustaceanvim.os')
33

44
local cargo = {}
55

6+
---@param path string The directory to search upward from
7+
---@param callback? fun(cargo_crate_dir: string?, cargo_metadata: table?) If `nil`, this function runs synchronously
8+
---@return string? cargo_crate_dir (if `callback ~= nil` and successful)
9+
---@return table? cargo_metadata (if `callback ~= nil` and successful)
10+
local function get_cargo_metadata(path, callback)
11+
---@diagnostic disable-next-line: missing-fields
12+
local cargo_crate_dir = vim.fs.dirname(vim.fs.find({ 'Cargo.toml' }, {
13+
upward = true,
14+
path = path,
15+
})[1])
16+
if vim.fn.executable('cargo') ~= 1 then
17+
return callback and callback(cargo_crate_dir) or cargo_crate_dir
18+
end
19+
local cmd = { 'cargo', 'metadata', '--no-deps', '--format-version', '1' }
20+
if cargo_crate_dir ~= nil then
21+
cmd[#cmd + 1] = '--manifest-path'
22+
cmd[#cmd + 1] = vim.fs.joinpath(cargo_crate_dir, 'Cargo.toml')
23+
end
24+
25+
---@param sc vim.SystemCompleted
26+
local function on_exit(sc)
27+
if sc.code ~= 0 then
28+
return callback and callback(cargo_crate_dir) or cargo_crate_dir
29+
end
30+
local ok, cargo_metadata_json = pcall(vim.fn.json_decode, sc.stdout)
31+
if ok and cargo_metadata_json then
32+
return callback and callback(cargo_crate_dir, cargo_metadata_json) or cargo_crate_dir, cargo_metadata_json
33+
end
34+
return callback and callback(cargo_crate_dir) or cargo_crate_dir
35+
end
36+
37+
if callback then
38+
vim.uv.fs_stat(path, function(_, stat)
39+
vim.system(cmd, {
40+
cwd = stat and path or cargo_crate_dir or vim.fn.getcwd(),
41+
}, on_exit)
42+
end)
43+
else
44+
local sc = vim
45+
.system(cmd, {
46+
cwd = vim.uv.fs_stat(path) and path or cargo_crate_dir or vim.fn.getcwd(),
47+
})
48+
:wait()
49+
return on_exit(sc)
50+
end
51+
end
52+
53+
---The default implementation used for `vim.g.rustaceanvim.server.root_dir`
54+
---@param file_name string
55+
---@param callback? fun(root_dir: string | nil) If `nil`, this function runs synchronously
56+
---@return string | nil root_dir (if `callback ~= nil` and successful)
57+
local function default_get_root_dir(file_name, callback)
58+
local path = file_name:find('%.rs$') and vim.fs.dirname(file_name) or file_name
59+
if not path then
60+
return nil
61+
end
62+
63+
---@param cargo_crate_dir? string
64+
---@param cargo_metadata? table
65+
---@return string | nil root_dir
66+
local function root_dir(cargo_crate_dir, cargo_metadata)
67+
return cargo_metadata and cargo_metadata.workspace_root
68+
or cargo_crate_dir
69+
or vim.fs.dirname(vim.fs.find({ 'rust-project.json' }, {
70+
upward = true,
71+
path = path,
72+
})[1])
73+
end
74+
if callback then
75+
get_cargo_metadata(path, function(cargo_crate_dir, cargo_metadata)
76+
callback(root_dir(cargo_crate_dir, cargo_metadata))
77+
end)
78+
else
79+
local cargo_crate_dir, cargo_metadata = get_cargo_metadata(path)
80+
return root_dir(cargo_crate_dir, cargo_metadata)
81+
end
82+
end
83+
684
---Checks if there is an active client for file_name and returns its root directory if found.
785
---@param file_name string
886
---@return string | nil root_dir The root directory of the active client for file_name (if there is one)
@@ -29,51 +107,24 @@ end
29107
---client root is found, returns the result of evaluating `config.root_dir`.
30108
---@param config rustaceanvim.lsp.ClientConfig
31109
---@param file_name string
110+
---@param callback? fun(root_dir: string?) If `nil`, this function runs synchronously
32111
---@return string | nil root_dir
33-
function cargo.get_config_root_dir(config, file_name)
112+
function cargo.get_config_root_dir(config, file_name, callback)
34113
local reuse_active = get_mb_active_client_root(file_name)
35114
if reuse_active then
36115
return reuse_active
37116
end
38-
39-
local config_root_dir = config.root_dir
40-
if type(config_root_dir) == 'function' then
41-
return config_root_dir(file_name, cargo.get_root_dir)
117+
if type(config.root_dir) == 'function' then
118+
local root_dir = config.root_dir(file_name, default_get_root_dir)
119+
return callback and callback(root_dir) or root_dir
120+
elseif type(config.root_dir) == 'string' then
121+
local root_dir = config.root_dir
122+
---@cast root_dir string
123+
return callback and callback(root_dir) or root_dir
42124
else
43-
return config_root_dir
44-
end
45-
end
46-
47-
---@param path string The directory to search upward from
48-
---@return string? cargo_crate_dir
49-
---@return table? cargo_metadata
50-
local function get_cargo_metadata(path)
51-
---@diagnostic disable-next-line: missing-fields
52-
local cargo_crate_dir = vim.fs.dirname(vim.fs.find({ 'Cargo.toml' }, {
53-
upward = true,
54-
path = path,
55-
})[1])
56-
if vim.fn.executable('cargo') ~= 1 then
57-
return cargo_crate_dir
58-
end
59-
local cmd = { 'cargo', 'metadata', '--no-deps', '--format-version', '1' }
60-
if cargo_crate_dir ~= nil then
61-
cmd[#cmd + 1] = '--manifest-path'
62-
cmd[#cmd + 1] = vim.fs.joinpath(cargo_crate_dir, 'Cargo.toml')
125+
callback = callback and vim.schedule_wrap(callback)
126+
return default_get_root_dir(file_name, callback)
63127
end
64-
local sc = vim
65-
.system(cmd, {
66-
cwd = vim.uv.fs_stat(path) and path or cargo_crate_dir or vim.fn.getcwd(),
67-
})
68-
:wait()
69-
if sc.code ~= 0 then
70-
return cargo_crate_dir
71-
end
72-
local ok, cargo_metadata_json = pcall(vim.fn.json_decode, sc.stdout)
73-
if ok and cargo_metadata_json then
74-
return cargo_crate_dir, cargo_metadata_json
75-
end
76-
return cargo_crate_dir
77128
end
78129

79130
---@param buf_name? string
@@ -99,22 +150,4 @@ function cargo.get_rustc_edition(buf_name)
99150
return package and package.edition or default_edition
100151
end
101152

102-
---The default implementation used for `vim.g.rustaceanvim.server.root_dir`
103-
---@param file_name string
104-
---@return string | nil root_dir
105-
function cargo.get_root_dir(file_name)
106-
local path = file_name:find('%.rs$') and vim.fs.dirname(file_name) or file_name
107-
if not path then
108-
return nil
109-
end
110-
local cargo_crate_dir, cargo_metadata = get_cargo_metadata(path)
111-
return cargo_metadata and cargo_metadata.workspace_root
112-
or cargo_crate_dir
113-
---@diagnostic disable-next-line: missing-fields
114-
or vim.fs.dirname(vim.fs.find({ 'rust-project.json' }, {
115-
upward = true,
116-
path = path,
117-
})[1])
118-
end
119-
120153
return cargo

lua/rustaceanvim/config/check.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ function M.validate(cfg)
9999
cmd = { server.cmd, { 'function', 'table' } },
100100
standalone = { server.standalone, 'boolean' },
101101
settings = { server.settings, { 'function', 'table' }, true },
102-
root_dir = { server.root_dir, { 'function', 'string' } },
102+
root_dir = { server.root_dir, { 'nil', 'function', 'string' } },
103103
})
104104
if not ok then
105105
return false, err

lua/rustaceanvim/config/internal.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
local types = require('rustaceanvim.types.internal')
2-
local cargo = require('rustaceanvim.cargo')
32
local config = require('rustaceanvim.config')
43
local executors = require('rustaceanvim.executors')
54
local os = require('rustaceanvim.os')
@@ -285,8 +284,9 @@ local RustaceanDefaultConfig = {
285284
return { exepath_or_binary('rust-analyzer'), '--log-file', RustaceanConfig.server.logfile }
286285
end,
287286

288-
---@type string | fun(filename: string, default: fun(filename: string):string|nil):string|nil
289-
root_dir = cargo.get_root_dir,
287+
--- Defaults to `nil`, which means rustaceanvim will use the built-in async root directory detection
288+
---@type nil | string | fun(filename: string, default: fun(filename: string):string|nil):string|nil
289+
root_dir = nil,
290290

291291
ra_multiplex = {
292292
---@type boolean

0 commit comments

Comments
 (0)