I had high hopes that JetBrains would relent on their decision to avoid implementing a Language Server Protocol server. There is a 3rd party implementation out there, but it needs a lot of help. I realize this cuts into their core business... but it'd sure make using Vim to write Kotlin a more pleasant experience.
When I last used them for Rust about two years ago they were racing neck and neck against a language server being refactored to share
a backend with the reference compiler.
I switched to the language server because I felt running the actual compiler will in the long term always work better than writing your own parser. I was on the Jetbrains student discount, so I made a small recurring contribution to the rust language server project.
I think in retrospect that was the correct decision. The language server team has done great work, and they support things impractical to do with just parsing like autocompleting functions defined by macros.
> running the actual compiler will in the long term always work better than writing your own parser.
No, it won't. A compiler and an IDE code analyzer have completely different non-intersecting goals.
As an example, consider error recovery. Where a compiler can just fail with an error, in an IDE you need to continue to provide full correct syntax highlighting, display all other potential errors and warnings, continue providing code analysis (including suggestions on how to fix the error) etc.
> The language server team has done great work, and they support things impractical to do with just parsing like autocompleting functions defined by macros.
None of these are provided by the language server team. These are provided by whoever wrote the code to analyze your stuff and provide data to the language server.
> No, it won't. A compiler and an IDE code analyzer have completely different non-intersecting goals.
That's the conventional wisdom. The rust team's theory was that they could make it work. They did.
> As an example, consider error recovery. Where a compiler can just fail with an error, in an IDE you need to continue to provide full correct syntax highlighting, display all other potential errors and warnings, continue providing code analysis (including suggestions on how to fix the error) etc.
Possibly the Rust compiler's unusual prioritization of friendly error messages with helpful suggestions meant this wasn't as big a difference as you thought?
> None of these are provided by the language server team. These are provided by whoever wrote the code to analyze your stuff and provide data to the language server.
That's how it works for many languages: the language server implementation is a thin wrapper around an existing tool. That's not the case for rust. I've watched the work the Rust language server team did on their language server implementation and the compiler. It was impressive.
More to the point. Sadly I can't locate a great article outlining all the problems, bt these quotes will do
From rust-analyzer's main contributor [1]
=== start quote ===
The essential complexity for a server is pretty high. It is known that compilers are complicated, and a language server is a compiler and then some.
First, like a compiler, a language server needs to fully understand the language, it needs to be able to distinguish between valid and invalid programs. However, while for invalid programs a batch compiler is allowed to emit an error message and exit promptly, a language server must analyze any invalid program as best as it can. Working with incomplete and invalid programs is the first complication of a language server in comparison to a compiler.
Second, while a batch compiler is a pure function which transforms source text into machine code, a language server has to work with a code base which is constantly being modified by the user. It is a compiler with a time dimension, and evolution of state over time is one of the hardest problems in programming.
Third, a batch compiler is optimized for maximum throughput, while a language server aims to minimize latency (while not completely forgoing throughput). Adding a latency requirement doesn’t mean that you need to optimize harder. Rather, it means that you generally need to turn the architecture on its head to have an acceptable latency at all.
=== end quote ===
Rust analyzer uses a custom parser [2]
=== start quote ===
a hand-written recursive descent parser, which produces a sequence of events like "start node X", "finish node Y". It works similarly to kotlin's parser, which is a good source of inspiration for dealing with syntax errors and incomplete input. Original libsyntax parser is what we use for the definition of the Rust language. TreeSink and TokenSource traits bridge the tree-agnostic parser from grammar with rowan trees.
I once heard that an editor spends the majority of its time in an error state, and only periodically someone stops typing in hopefully(!) a valid state. Thinking how to generate helpful recovery states in an ide's parser sounds like an incredible amount of work
While that's absolutely true, I'd be totally fine with what an LSP _does_ offer. Auto imports, go to definition, list usages, etc. I don't need the heavy refactoring tools.