I am going to write at least N posts in this thread, on slightly different topics. Here’s the first one!

Let’s discuss maintenance costs!

The perfect end-state I envision is to have a single rust-analyzer library, which is used by both command-line compiler as well as the language server. The library deals strictly with front-end bits of Rust (it takes source code as an input and produces checked MIR as a result), and, as a consequence, does not depend on LLVM and builds on stable Rust. This state I think would be much more productive than the current setup: if you want to hack on Rust frontend, you can just cargo test without building any C code, without bootstraping, and without fearing that the tests will fail on some obscure architecture. The tests themselves could be pretty fast as well: proper factoring of rust-analyzer will require a careful isolation of all IO bits, which would mean that tests won’t be doing any IO, which should make it harder to write slow tests.

The problem with this pretty picture, of course, is that we need somehow to get there first, and there’s certain fear that building a second frontend would be too much of cost.

I’d like to argue though that RLS today already is pretty costly in terms of maintenance.

The first reason for this is that the significant chunk of features that RLS provides is actually handled by Racer, and Racer itself is an alternative rust frontend of sorts. The work to maintain racer is large ( it has more commits and contributors then RLS EDIT: this is apples to oranges, kudos to @kngwyu for pointing out) and, when we switch to compiler driven completion, all that work will become useless.

The second reason for this is that working on RLS itself is super hard. RLS links in Cargo, Clippy, rustfmt and uses private rustc APIs, so it is next to impossible to build. Like, the versions of all those things need to align in just the right way, and you also need to have a proper Rust compiler (which might not be even released as nightly yet). Half of the commits to RLS is “update X”, and there’s also a busy-work process of updating RLS in rust-lang/rust.

As an example, I’ve implemented “run the test under cursor” feature both in RLS and in libsyntax2 based language server. The implementation in libsyntax2 is more powerful, was easier to write, and is much better covered by tests.

In RLS, I had to use regular expressions to detect test functions, because there’s no syntax trees in RLS. Then I had to wrestle with a finky test. The test was later commented out, so had to wrestle more and more to actually make it pass. This took about a month IIRC?

In libsyntax2, I’ve wrote a small traversal of AST, (which also handles fn main ), a unit test for it, a bit of plumbing for LSP and adding --package arg to command line (which RLS does not do), and an integration test, which checks that this all works together and calls cargo metadata fine.

The fact that libsyntax2 and RLS repositories have approximately the same number of Rust source code lines, if you exclude tests and generated code, also hints that adding a line of code to RLS is very expensive.