A lot of things described in this article applicable not only for Rust, but for almost any language. Like it's obvious that requests should be handled asynchronously and that conversions from/to UTF-16 are needed. But it's actually not so hard.
I have written a language server for my language too. The hardest thing was to find a way allowing providing useful autocompletion for a document in edited state, when it's not syntactically-correct. This is the trickiest part how to deal with such incorrectness without missing all the context necessary.
I've not written a language server but have written a language plugin for IntelliJ.
I started with writing a correct recursive descent parser. I then extended it to detect, report, and recover from common syntax errors as I encountered them so that the parser is robust. And adding a parser test case for each of these (e.g. one test for each branch through an EBNF construction).
Some examples are:
1. missing keywords when the keyword can be detected from the current context (e.g. missing semicolon at the end of a statement);
2. using the wrong token (e.g. `:` instead of `::` in a C++ namespace qualified name);
3. detecting and ignoring whitespace in a whitespace-sensitive qualification (e.g. in XML QNames);
4. keeping in the prolog state (where functions are defined) when there are errors so that functions after the error don't get lost;
5. lexing incomplete literals like `10e` so they can be handled as integers in the parser and emitting an error for them.