Skip to content

Commit

Permalink
feat(lsp): auto-connect to ra-multiplex if running
Browse files Browse the repository at this point in the history
  • Loading branch information
mrcjkb committed Dec 24, 2024
1 parent 7a1511b commit b394709
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 19 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,18 @@ by setting the rust-analyzer

</details>

<details>
<summary>
<b>ra-multiplex</b>
</summary>

On Linux and MacOS, rustaceanvim can auto-detect and connect to a
running [ra-multiplex](https://github.com/pr2502/ra-multiplex) server.
By default, it will try to do so automatically if the `vim.g.rustaceanvim.server.cmd`
option is unset.
See also `:h rustaceanvim.ra_multiplex`.

</details>
<!-- markdownlint-restore -->

## :gear: Advanced configuration
Expand Down
18 changes: 18 additions & 0 deletions doc/rustaceanvim.txt
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ rustaceanvim.lsp.ClientOpts *rustaceanvim.lsp.ClientOpts*
The directory to use for the attached LSP.
Can be a function, which may return nil if no server should attach.
The second argument contains the default implementation, which can be used for fallback behavior.
{ra_multiplex?} (rustaceanvim.ra_multiplex.Opts)
Options for connecting to ra-multiplex.
{settings?} (table|fun(project_root:string|nil,default_settings:table):table)
Setting passed to rust-analyzer.
Defaults to a function that looks for a `rust-analyzer.json` file or returns an empty table.
Expand All @@ -314,6 +316,22 @@ rustaceanvim.lsp.ClientOpts *rustaceanvim.lsp.ClientOpts*
Default: 'error'


rustaceanvim.ra_multiplex.Opts *rustaceanvim.ra_multiplex.Opts*

Fields: ~
{enable?} (boolean)
Whether to enable ra-multiplex auto-discovery.
Default: `true` if `server.cmd` is not set, otherwise `false`.
If enabled, rustaceanvim will try to detect if an ra-multiplex server is running
and connect to it (Linux and MacOS only).
If auto-discovery does not work, you can set `server.cmd` to a function that
returns an LSP RPC client factory (see |vim.lsp.rpc.connect|).
{host?} (string)
The host to connect to. Default: '127.0.0.1'
{port?} (integer)
The port to connect to. Default: 27631


rustaceanvim.server.status_notify_level*rustaceanvim.server.status_notify_level*

Type: ~
Expand Down
1 change: 1 addition & 0 deletions doc/tags
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ rustaceanvim.lsp.ClientOpts rustaceanvim.txt /*rustaceanvim.lsp.ClientOpts*
rustaceanvim.lsp_server_health_status rustaceanvim.txt /*rustaceanvim.lsp_server_health_status*
rustaceanvim.mason mason.txt /*rustaceanvim.mason*
rustaceanvim.neotest rustaceanvim.txt /*rustaceanvim.neotest*
rustaceanvim.ra_multiplex.Opts rustaceanvim.txt /*rustaceanvim.ra_multiplex.Opts*
rustaceanvim.rustc.Opts rustaceanvim.txt /*rustaceanvim.rustc.Opts*
rustaceanvim.test_executor_alias rustaceanvim.txt /*rustaceanvim.test_executor_alias*
rustaceanvim.tools.Opts rustaceanvim.txt /*rustaceanvim.tools.Opts*
Expand Down
19 changes: 19 additions & 0 deletions lua/rustaceanvim/config/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ vim.g.rustaceanvim = vim.g.rustaceanvim
---The second argument contains the default implementation, which can be used for fallback behavior.
---@field root_dir? string | fun(filename: string, default: fun(filename: string):string|nil):string|nil
---
---Options for connecting to ra-multiplex.
---@field ra_multiplex? rustaceanvim.ra_multiplex.Opts
---
---Setting passed to rust-analyzer.
---Defaults to a function that looks for a `rust-analyzer.json` file or returns an empty table.
---See https://rust-analyzer.github.io/manual.html#configuration.
Expand All @@ -200,6 +203,22 @@ vim.g.rustaceanvim = vim.g.rustaceanvim
---
---@see vim.lsp.ClientConfig

---@class rustaceanvim.ra_multiplex.Opts
---
---Whether to enable ra-multiplex auto-discovery.
---Default: `true` if `server.cmd` is not set, otherwise `false`.
---If enabled, rustaceanvim will try to detect if an ra-multiplex server is running
---and connect to it (Linux and MacOS only).
---If auto-discovery does not work, you can set `server.cmd` to a function that
---returns an LSP RPC client factory (see |vim.lsp.rpc.connect|).
---@field enable? boolean
---
---The host to connect to. Default: '127.0.0.1'
---@field host? string
---
---The port to connect to. Default: 27631
---@field port? integer

---@alias rustaceanvim.server.status_notify_level 'error' | 'warning' | rustaceanvim.disable

---@alias rustaceanvim.disable false
Expand Down
29 changes: 22 additions & 7 deletions lua/rustaceanvim/config/internal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ local server_config = require('rustaceanvim.config.server')

local RustaceanConfig

local rustaceanvim = vim.g.rustaceanvim or {}
local rustaceanvim_opts = type(rustaceanvim) == 'function' and rustaceanvim() or rustaceanvim

---@class rustaceanvim.internal.RAInitializedStatus : rustaceanvim.RAInitializedStatus
---@field health rustaceanvim.lsp_server_health_status
---@field quiescent boolean inactive?
Expand Down Expand Up @@ -278,6 +281,15 @@ local RustaceanDefaultConfig = {
---@type string | fun(filename: string, default: fun(filename: string):string|nil):string|nil
root_dir = cargo.get_root_dir,

ra_multiplex = {
---@type boolean
enable = vim.tbl_get(rustaceanvim_opts, 'server', 'cmd') == nil,
---@type string
host = '127.0.0.1',
---@type integer
port = 27631,
},

--- standalone file support
--- setting it to false may improve startup time
---@type boolean
Expand Down Expand Up @@ -393,20 +405,23 @@ local RustaceanDefaultConfig = {
-- debug info
was_g_rustaceanvim_sourced = vim.g.rustaceanvim ~= nil,
}
local rustaceanvim = vim.g.rustaceanvim or {}
local opts = type(rustaceanvim) == 'function' and rustaceanvim() or rustaceanvim
for _, executor in pairs { 'executor', 'test_executor', 'crate_test_executor' } do
if opts.tools and opts.tools[executor] and type(opts.tools[executor]) == 'string' then
opts.tools[executor] = assert(executors[opts.tools[executor]], 'Unknown RustaceanExecutor')
if
rustaceanvim_opts.tools
and rustaceanvim_opts.tools[executor]
and type(rustaceanvim_opts.tools[executor]) == 'string'
then
rustaceanvim_opts.tools[executor] =
assert(executors[rustaceanvim_opts.tools[executor]], 'Unknown RustaceanExecutor')
end
end

---@type rustaceanvim.Config
RustaceanConfig = vim.tbl_deep_extend('force', {}, RustaceanDefaultConfig, opts)
RustaceanConfig = vim.tbl_deep_extend('force', {}, RustaceanDefaultConfig, rustaceanvim_opts)

-- Override user dap.adapter config in a backward compatible way
if opts.dap and opts.dap.adapter then
local user_adapter = opts.dap.adapter
if rustaceanvim_opts.dap and rustaceanvim_opts.dap.adapter then
local user_adapter = rustaceanvim_opts.dap.adapter
local default_adapter = types.evaluate(RustaceanConfig.dap.adapter)
if
type(user_adapter) == 'table'
Expand Down
45 changes: 33 additions & 12 deletions lua/rustaceanvim/lsp/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -184,16 +184,6 @@ Starting rust-analyzer client in detached/standalone mode (with reduced function
lsp_start_config.settings = get_start_settings(bufname, root_dir, client_config)
configure_file_watcher(lsp_start_config)

-- rust-analyzer treats settings in initializationOptions specially -- in particular, workspace_discoverConfig
-- so copy them to init_options (the vim name)
-- so they end up in initializationOptions (the LSP name)
-- ... and initialization_options (the rust name) in rust-analyzer's main.rs
lsp_start_config.init_options = vim.tbl_deep_extend(
'force',
lsp_start_config.init_options or {},
vim.tbl_get(lsp_start_config.settings, 'rust-analyzer')
)

-- Check if a client is already running and add the workspace folder if necessary.
for _, client in pairs(rust_analyzer.get_active_rustaceanvim_clients()) do
if root_dir and not is_in_workspace(client, root_dir) then
Expand All @@ -215,6 +205,26 @@ Starting rust-analyzer client in detached/standalone mode (with reduced function
end

local rust_analyzer_cmd = types.evaluate(client_config.cmd)

local ra_multiplex = lsp_start_config.ra_multiplex
if ra_multiplex.enable then
local ok, running_ra_multiplex = pcall(function()
local result = vim.system({ 'pgrep', 'ra-multiplex' }):wait().code
return result == 0
end)
if ok and running_ra_multiplex then
rust_analyzer_cmd = vim.lsp.rpc.connect(ra_multiplex.host, ra_multiplex.port)
local ra_settings = lsp_start_config.settings['rust-analyzer'] or {}
ra_settings.lspMux = ra_settings.lspMux
or {
version = '1',
method = 'connect',
server = 'rust-analyzer',
}
lsp_start_config.settings['rust-analyzer'] = ra_settings
end
end

-- special case: rust-analyzer has a `rust-analyzer.server.path` config option
-- that allows you to override the path via .vscode/settings.json
local server_path = vim.tbl_get(lsp_start_config.settings, 'rust-analyzer', 'server', 'path')
Expand Down Expand Up @@ -287,7 +297,17 @@ Starting rust-analyzer client in detached/standalone mode (with reduced function
end
end

return vim.lsp.start(lsp_start_config)
-- rust-analyzer treats settings in initializationOptions specially -- in particular, workspace_discoverConfig
-- so copy them to init_options (the vim name)
-- so they end up in initializationOptions (the LSP name)
-- ... and initialization_options (the rust name) in rust-analyzer's main.rs
lsp_start_config.init_options = vim.tbl_deep_extend(
'force',
lsp_start_config.init_options or {},
vim.tbl_get(lsp_start_config.settings, 'rust-analyzer')
)

return vim.lsp.start(vim.print(lsp_start_config))
end

---Stop the LSP client.
Expand Down Expand Up @@ -337,7 +357,8 @@ M.set_target_arch = function(bufnr, target)
restart(bufnr, { exclude_rustc_target = target }, function(client)
rustc.with_rustc_target_architectures(function(rustc_targets)
if rustc_targets[target] then
local ra = client.config.settings['rust-analyzer']
local ra = client.config.settings['rust-analyzer'] or {}
---@diagnostic disable-next-line: inject-field
ra.cargo = ra.cargo or {}
ra.cargo.target = target
compat.client_notify(client, 'workspace/didChangeConfiguration', { settings = client.config.settings })
Expand Down

0 comments on commit b394709

Please sign in to comment.