Multiplex server for rust-analyzer, allows multiple LSP clients (editor windows) to share a single rust-analyzer instance per cargo workspace

Overview

ra-multiplex   Latest Version

Multiplex server for rust-analyzer, allows multiple LSP clients (editor windows) to share a single rust-analyzer instance per cargo workspace.

How it works

The project has two binaries, ra-multiplex which is a thin wrapper that acts like rust-analyzer but only connects to a TCP socket at 127.0.0.1:27631 and pipes stdin and stdout through it.

The second binary ra-multiplex-server will listen on :27631 and spawn the rust-analyzer server, depending on the working directory the ra-multiplex client was spawned from it can reuse an already spawned rust-analyzer instance. It detects workspace root as the furthermost ancestor directory containing a Cargo.toml file. If the automatic workspace detection fails or if you're not using rust-analyzer as a server you can create a marker file .ra-multiplex-workspace-root in the directory you want to use as a workspace root, the first ancestor directory containing this file will be used.

Because neither LSP nor rust-analyzer itself support multiple clients per server ra-multiplex-server caches the handshake messages and modifies IDs of requests & responses to track which response belongs to which client. Because not all messages can be tracked this way it drops some, notably it drops any requests from the server, this appears to not be a problem with coc-rust-analyzer in neovim but YMMV.

If you have any problems you're welcome to open issues on this repository.

How to use

Build the project with

$ cargo build --release

Run the ra-multiplex-server, make sure that rust-analyzer is in your PATH:

$ which rust-analyzer
/home/user/.cargo/bin/rust-analyzer
$ target/release/ra-multiplex-server

Configure your editor to use ra-multiplex as rust-analyzer, for example for CoC in neovim edit ~/.config/nvim/coc-settings.json, add:

{
    "rust-analyzer.serverPath": "/path/to/ra-multiplex"
}

Configuration

Configuration is stored in a TOML file in your system's default configuration directory, for example ~/.config/ra-multiplex/config.toml. If you're not sure where that is on your system starting either ra-multiplex or ra-multiplex-server without a config file present will print a warning with the expected path.

Note that the configuration file is likely not necessary and ra-multiplex should be usable with all defaults.

Example configuration file:

log_filters = "info" # whether `ra-multiplex` client should process any arguments # # by default this is set to `false`, in this case `ra-multiplex` can be used as # a drop-in replacement for `rust-analyzer` but no other server. for more # information see the "Other LSP servers" section of the README. arg_parsing = false">
# this is an example configuration file for ra-multiplex
#
# all configuration options here are set to their default value they'll have if
# they're not present in the file or if the config file is missing completely.

# time in seconds after which a rust-analyzer server instance with no clients
# connected will get killed to save system memory.
#
# you can set this option to `false` for infinite timeout
instance_timeout = 300 # after 5 minutes

# time in seconds how long to wait between the gc task checks for disconnected
# clients and possibly starts a timeout task. the value must be at least 1.
gc_interval = 10 # every 10 seconds

# ip address and port on which ra-multiplex-server listens
#
# the default "127.0.0.1" only allows connections from localhost which is
# preferred since the protocol doesn't worry about security.
# ra-multiplex-server expects the filesystem structure and contents to be the
# same on its machine as on ra-multiplex's machine. if you want to run the
# server on a different computer it's theoretically possible but at least for
# now you're on your own.
#
# ports below 1024 will typically require root privileges and should be
# avoided, the default was picked at random, this only needs to change if
# another application happens to collide with ra-multiplex.
listen = ["127.0.0.1", 27631] # localhost & some random unprivileged port

# ip address and port to which ra-multiplex will connect to
#
# this should usually just match the value of `listen`
connect = ["127.0.0.1", 27631] # same as `listen`

# default log filters
#
# RUST_LOG env variable overrides this option, both use the same syntax which
# is documented in the `env_logger` documentation here:
# 
log_filters = "info"

# whether `ra-multiplex` client should process any arguments
#
# by default this is set to `false`, in this case `ra-multiplex` can be used as
# a drop-in replacement for `rust-analyzer` but no other server. for more
# information see the "Other LSP servers" section of the README.
arg_parsing = false

Other LSP servers

Using other servers requires a bit more setup. First enable the arg_parsing option in ~/.config/ra-multiplex/config.toml:

arg_parsing = true

Then for each server you want to use (including rust-analyzer) create a wrapper script that passes the right arguments like in the following example. We're using the standard cargo install directories so update the examples for your needs.

Wrapper script rust-analyzer-proxy:

#!/bin/sh
exec ~/.cargo/bin/ra-multiplex --server ~/.cargo/bin/rust-analyzer -- $@

Wrapper script clangd-proxy:

#!/bin/sh
exec ~/.cargo/bin/ra-multiplex --server /usr/bin/clangd -- --log=error $@

Configure LSP client to use the wrapper script, for CoC ~/.config/nvim/coc-settings.json:

{
    "rust-analyzer.serverPath": "/home/user/.cargo/bin/rust-analyzer-proxy",
    "clangd.path": "/home/user/.cargo/bin/clangd-proxy"
}
Comments
  • Closing an editor kills the server

    Closing an editor kills the server

    Thanks for the great project, it's a huge help for me, just one issue:

    Closing an editor normally, e.g. in my case in helix via :q will cause the server to shutdown:

    image

    And ra will have to reparse the directory the next time I open it.

    If I close by killing the window through my window manager, the server stays active and runs through the intended process of waiting 5 minutes of no activity before shutting down.

    Also a minor one but the [DEBUG] and [INFO] messages come through as [ERROR].

    opened by jackos 11
  • Usability improvements

    Usability improvements

    Closes #9

    1. When ra-multiplex-server fails to invoke the lsp server, display the attempted command.
    2. Error if ra-multiplex-server is invoked with the ra-mux-server argument. It should only be passed to the ra-multiplex client.
    3. Add ra-multiplex-server --help and -h.

    I initially added help text for the client, but I removed it because I think ra-multiplex --help should proxy to rust-analyzer --help.

    opened by yaymukund 5
  • `--ra-mux-server` argument applies to `ra-multiplex` and not `ra-multiplex-server`

    `--ra-mux-server` argument applies to `ra-multiplex` and not `ra-multiplex-server`

    Thank you for building this crate. It's very handy.

    Problem

    $ ra-multiplex-server --ra-mux-server /path/to/custom/rust-analyzer &
    $ ra-multiplex <...>
    

    then I get:

    [INFO ] [32830] client connected
    [ERROR] [32830] client error: spawn ra instance
    
         Caused by:
             0: couldn't spawn rust-analyzer
             1: No such file or directory (os error 2)
    

    Passing --ra-mux-server to ra-multiplex-server doesn't seem to work. ra-multiplex cannot find rust-analyzer.

    Solution

    You have to pass it via the client. To fix:

    $ ra-multiplex-server --ra-mux-server /path/to/custom/rust-analyzer &
    $ ra-multiplex --ra-mux-server /path/to/custom/rust-analyzer <...>
    

    Thoughts

    1. Does passing --ra-mux-server to the server do anything? If not, should it error?
    2. If --ra-mux-server is a valid option to the server: In the absence of a client --ra-mux-server, the server's configuration should take priority.
    3. Maybe we could pass ra-mux-server via the config?
    4. Maybe this could be spelled out a bit more in the docs? We could kill two birds with one stone by adding a --help?

    I am happy to submit PRs for any/all of these. I just wanted to ask first because the intended/correct behavior aren't clear to me.

    opened by yaymukund 2
  • allow other LSP servers

    allow other LSP servers

    allow using ra-multiplex with other LSP servers than rust-analyzer. this would require:

    • [x] a way of telling the ra-multiplex client which LSP server to use
    • [x] more general workspace/project root searching

    for the first we can probably use argv[0] since that one is not useful for anything.

    for the second we could continue searching for Cargo.toml files if the requested server is rust-analyzer and maybe for a .git directory otherwise. either way we should handle not finding any workspace root gracefully and fall back to just using the provided cwd. the only downside is the servers might not get shared every time depending on where users' editor gets launched from. we could also add an option to mark the workspace root with a .ra-multiplex-workspace-root file

    feature request 
    opened by pr2502 2
  • ra-multiplex on nvim with rust-tools.nvim

    ra-multiplex on nvim with rust-tools.nvim

    I couldnt get this to work and wasn't sure what steps to take to investigate. Running the ra-multiplex-server doesn't read the config in my home directory (~/config/ra-multiplex/config.toml)

    ❯ ra-multiplex-server -h
    [WARN ] cannot load config: cannot read config file `/Users/tonyalaribe.parity/Library/Application Support/ra-multiplex/config.toml`
    
        Caused by:
            No such file or directory (os error 2)
    ^C
    
    
    opened by tonyalaribe 1
  • Update Cargo.lock

    Update Cargo.lock

    b9a14e576f6239563681660b7634ac80183a71e9 bumped the version in Cargo.toml, but the corresponding value in Cargo.lock wasn't updated, which caused me issues when trying to package this in a Nix expression.

    opened by nerosnm 1
  • conditionally compile unix specific parts

    conditionally compile unix specific parts

    This allows ra-multiplex to build on Windows. I tested it with VSCode by setting "rust-analyzer.server.path" to the ra-multiplexer.exe path. It appears to work very well with multiple VSCode windows open on the same project!


    Some notes from setting it up:

    Unless if I manually create a C:\Users\Michael\AppData\Roaming\ra-multiplex\config\config.toml

    # config.toml
    arg_parsing = true
    

    the server eventually logs errors like the following

    [ERROR] [C:\Program Files\Microsoft VS Code 1972] error reading from stdout: parsing header
    
        Caused by:
            malformed header, missing \r\n
    

    because r-a's VSCode extension also calls ra-multiplex.exe --version, but without arg_parsing = true, the extension calling the client never receives output (the version), errors like above, then closes that short-lived client.

    I haven't made any changes related to this in this pull request (like documentation or different defaults, e.g. always having arg parsing), but I may as well mention if anyone else hits this / before I forget :)

    opened by memoryruins 1
  • Naively, ra-multiplex-server appears to fail to start when the config file is missing

    Naively, ra-multiplex-server appears to fail to start when the config file is missing

    If ~/.config/ra-multiplex/config.toml does not exist, then ra-multiplex-server logs a warning. However, it doesn't indicate in the warning that everything is fine. This led me to believe ra-multiplex-server failed to start.

    I recommend logging a message following the warning with something like "Proceeding with default configuration." Something like the following would be nice.

    % ra-multiplex-server
    [WARN ] cannot load config: cannot read config file `/.../.config/ra-multiplex/config.toml`
    
        Caused by:
            No such file or directory (os error 2)
    [WARN ] Successfully started with default config
    
    opened by c6c7 0
  • clippy fixes

    clippy fixes

    tiny fix for:

        Checking ra-multiplex v0.2.0 (src/ra-multiplex)
    warning: deref which would be done by auto-deref
       --> src/bin/server/lsp.rs:164:9
        |
    164 |         &*self.bytes
        |         ^^^^^^^^^^^^ help: try this: `&self.bytes`
        |
        = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref
        = note: `#[warn(clippy::explicit_auto_deref)]` on by default
    
    warning: deref which would be done by auto-deref
       --> src/bin/server/lsp.rs:182:26
        |
    182 |         writer.write_all(&*self.bytes).await?;
        |                          ^^^^^^^^^^^^ help: try this: `&self.bytes`
        |
        = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref
    
    warning: `ra-multiplex` (bin "ra-multiplex-server") generated 2 warnings
        Finished dev [unoptimized + debuginfo] target(s) in 0.35s
    
    opened by yaymukund 0
  • VSCode calls `rust-analyzer --version` which breaks LSP parsing

    VSCode calls `rust-analyzer --version` which breaks LSP parsing

    This was mentioned in #3, VS Code will try to run rust-analyzer --version which works as expected in the sense that ra-multiplex spawns a new instance with the provided argument, however the output is just a version string and not a valid LSP message which results in a "malformed header" error like:

    [ERROR] [C:\Program Files\Microsoft VS Code 1972] error reading from stdout: parsing header
    
        Caused by:
            malformed header, missing \r\n
    
    compatibility 
    opened by pr2502 0
  • send expected response back to editor on shutdown request

    send expected response back to editor on shutdown request

    Some editors (at least that is the case for Helix), expect a response back from the server upon shutdown. Just closing the socket is not enough and cause the editor to hang.

    opened by xguerin 1
  • built-in process management

    built-in process management

    in order for this to be a full drop-in replacement it could be cool to:

    1. start ra-multiplex-server if it's not already running from within ra-multiplex
    2. have ra-multiplex-server shut down if it no longer holds any rust-analyzer instances
    feature request 
    opened by untitaker 2
Owner
max
max
Easily share data between terminal windows!

abra A tool that makes data sharing between terminal windows easy. abra can be used for displaying info about the current working directory, for split

Denis Isidoro 23 Oct 2, 2022
Small MQTT router. Allows creating multiple inputs/outputs and run action when input triggers.

MQRT Small MQTT router. Allows creating multiple inputs/outputs and run action when input triggers. Features multi-(input/output) multiple actions tie

Nazar Gondaruk 0 Jan 4, 2022
QUIC proxy that allows to use QUIC to connect to an SSH server without needing to patch the client or the server.

quicssh-rs ?? quicssh-rs is a QUIC proxy that allows to use QUIC to connect to an SSH server without needing to patch the client or the server. quicss

Jun Ouyang 18 May 5, 2023
Safe Rust crate for creating socket servers and clients with ease.

bitsock Safe Rust crate for creating socket servers and clients with ease. Description This crate can be used for Client <--> Server applications of e

Lorenzo Torres 3 Nov 25, 2021
Many modbus devices support only one or very few clients

Modbus TCP proxy Many modbus devices support only one or very few clients. This proxy acts as a bridge between the client and the modbus device. It ca

Tiago Coutinho 6 Aug 10, 2022
Fake rest is a fake API generator using a config file to help you develop clients.

About Fake-Rest is a fake API generator using a config file to help you develop clients. It's EASY AS HELL. Usage It's very simple to use. just create

Benyamin Eskandari 9 Feb 1, 2023
Easy per application transparent proxy built on cgroup.

cproxy can redirect TCP and UDP traffic made by a program to a proxy, without requiring the program supporting a proxy. Compared to many existi

Xiangru Lian 263 Dec 20, 2022
A crate for parsing HTTP rate limit headers as per the IETF draft

rate-limits A crate for parsing HTTP rate limit headers as per the IETF draft. Inofficial implementations like the Github rate limit headers are also

Matthias 3 Jul 9, 2022
IDP2P is a peer-to-peer identity protocol which enables a controller to create, manage and share its own proofs as well as did documents

IDP2P Experimental, inspired by ipfs, did:peer and keri Background See also (related topics): Decentralized Identifiers (DIDs) Verifiable Credentials

null 5 Oct 31, 2022
Download a file using multiple threads in parallel for faster download speeds.

multidl Download a file using multiple threads in parallel for faster download speeds. Uses 0 external dependencies. Usage Usage: multidl [--help] ADD

Divyanshu Agrawal 2 Sep 12, 2021
A fast, stable, efficient, and lightweight intranet penetration, port forwarding tool supports multiple connections, cascading proxy, and transmission encryption

A fast, stable, efficient, and lightweight intranet penetration, port forwarding tool supports multiple connections, cascading proxy, and transmission encryption

editso 1.3k Dec 30, 2022
DNS Server written in Rust for fun, see https://dev.to/xfbs/writing-a-dns-server-in-rust-1gpn

DNS Fun Ever wondered how you can write a DNS server in Rust? No? Well, too bad, I'm telling you anyways. But don't worry, this is going to be a fun o

Patrick Elsen 26 Jan 13, 2023
Dav-server-rs - Rust WebDAV server library. A fork of the webdav-handler crate.

dav-server-rs A fork of the webdav-handler-rs project. Generic async HTTP/Webdav handler Webdav (RFC4918) is defined as HTTP (GET/HEAD/PUT/DELETE) plu

messense 30 Dec 29, 2022
axum-server is a hyper server implementation designed to be used with axum framework.

axum-server axum-server is a hyper server implementation designed to be used with axum framework. Features Conveniently bind to any number of addresse

null 79 Jan 4, 2023
Jex Compiler Server - Server that runs Jex code

Server that compiles and runs Jex code.

furetur 3 Nov 18, 2021
A simple web server(and library) to display server stats over HTTP and Websockets/SSE or stream it to other systems.

x-server-stats A simple web server(and library) to display server stats over HTTP and Websockets/SSE or stream it to other systems. x-server(in x-serv

Pratyaksh 11 Oct 17, 2022
MPNC: Multipath single-Player NetCat

MPNC: Multipath single-Player NetCat TL;DR: A PoC to make your link aggregation works. Background We all know NC is useful in real world: # on server

Silver Bullet 1 Feb 6, 2022
A simple, single threaded and minimalistic port checker

A simple, single threaded and minimalistic port checker

null 1 Feb 12, 2022
An asynchronous dumb exporter proxy for prometheus. This aggregates all the metrics and exposes as a single scrape endpoint.

A dumb light weight asynchronous exporter proxy This is a dumb lightweight asynchronous exporter proxy that will help to expose multiple application m

Dark streams 3 Dec 4, 2022