Language servers and IDEs
After my post last week that I would be implementing the Rust plugin the classic way (that is, manually parsing, deriving type information, figuring out completions, refactorings, etc.), I received quite a few replies about the language server protocol and RLS specifically. I'd like to address RLS in more detail, since some of the replies were rather strongly-worded and sounded a bit patronizing.
The Good: in defense of language servers
Given that I will be criticising the language server protocol for the remainder of this post, I want to note that I'm not against the concept. I quite like it, actually. Language servers work great with lightweight editors: implement the protocol once and the editor becomes reasonably powerful for any language which has a corresponding language server. Visual Studio Code is a great example of an editor that benefits from language servers, which I guess is quite obvious given that the language server protocol was practically designed for VSCode. Still, others like Atom, Sublime, Vim and Emacs would also greatly benefit from the language server protocol. (Now, whether I would personally use it in Vim is a whole different story.)
From what I've seen in the language server protocol docs, that's pretty much what they're pushing the protocol for, at least for the time being. I think the language server protocol needs a lot more additions in order for it to be useful for IDEs. Which brings me to my point.
IDEs are not just editors
The thing is, IDEs have to support many things besides just the language itself; debugging and project management come to mind, but for those, the IDE doesn't necessarily need to know much about the language. For other things, such as static analysis, type hierarchy, dependencies, etc., the IDE needs to understand the language. And yes, you could argue that these functions can be implemented by the language servers, but the thing is, they're not just unavailable at the moment for some language servers, the protocol itself does not specify how they should be implemented.
LSP is incomplete
The protocol currently only specifies very basic functionality: autocompletions, quick fixes, semantic highlighting, finding uses, formatting and that just about covers it. This is nothing compared to things a good IDE can do. Specifically when it comes to code generation, I think that's where IDEs truly shine, but at the moment LSP does not specify how to do general code generation.
LSP is inextensible
Sure, individual language servers can extend the language server protocol with additional methods, such as rustDocument/diagnosticsBegin and rustDocument/diagnosticsEnd, but considering the point of the language server protocol is to reduce the amount of work necessary for tool developers, there is little incentive to keep track of every language server's extensions.
And The Ugly
Making an IDE jump over its own data structures is just plain ugly
Most large IDEs are built using a plugin-based architecture, in order to support many languages, and the core usually has some abstract language structures. Declarations and uses; the different kinds of language objects like functions, locals, classes, structures, etc. And just based on those, the IDE can implement a whole lot of functionality like go-to-declaration, rename, semantic highlighting, etc. All the language plugin needs to do is to populate these structures.
The problem with language servers is that they take all of that information away from the IDE. So in the end, it doesn't matter that you don't need to do parse and understand the language since the language server can do it for you, because you end up having to reinvent the wheel anyway by having to reimplement the core functionality to depend on the language server, rather than language support plugins.
How it can be improved
Just bashing on the protocol is not particularly useful for anyone, so I want to suggest a way many of these problems can be avoided: provide code structure information. Instead of trying to rip away the main component of an IDE from it, allow the IDE to work with the language server. There's no point in the IDE asking the language server what to highlight when the cursor is on a variable if the language server just tells the IDE where the declaration and where all the uses of that variable is in that document.
There's no need to remove any existing functionality either and the language server already computes all this information anyway! If it's exposed to the client, the client can decide if it wants/needs to use it or not.
And yet ignoring all of that, RLS is lacking in functionality
I decided to spend 72 hours last week implementing (a significant part of) the language server protocol in KDevelop in order to prove a point: RLS still doesn't even support many of the features that the language server protocol permits.
Completion support for locals works and not much else.
No completions for functions of something. (The ones shown are keyword completions from Kate.)
No completions could be triggered here. RLS returns an empty list.
No contextual completions: we are in a match block for foo, which is an Option type, which is an enum. It is reasonable to expect that we can autocomplete the enum values.
And this is just code completion. RLS returns empty lists for the documentSymbol and documentHighlight methods, hence why only the syntax highlighting from Kate is available. I haven't tried references, but since RLS computes that identically to documentHighlight, I expect it won't work either. The hover method doesn't work either. Even if the reason for those not working is that I missed setting up something (it happened initially with code completions requiring racer which needed an environment variable to be set), having the user set up all of this manually is not at all user-friendly.
Go-to-definition works. I haven't tried the formatting stuff, but that calls rustfmt, which the IDE can do by itself.
No other methods are supported by RLS at the time of writing. So things like searching for a struct or function in the project, getting information about a function signature, quick fixes, refactorings, etc. are not available.
My project proposal was to create a plugin providing good support for Rust in KDevelop. I intend to deliver on that project, but I do not think at its current state RLS provides good support. That may change in the future and I've shown a proof of concept that KDevelop can interact with a language server. Right now, I think I can do better.
Also, Rust's type system is inspired by the Hindley-Milner type inference algorithm. :D No way am I missing the opportunity to implement type inference. After all, Google Summer of Code is an opportunity for me to learn more about things I find interesting.