Rust LSPの構築が難しい理由
Why building a Rust LSP is hard

Rust Glancerの作者が、Rust LSPの構築がなぜ困難なのかを解説。LSPはコンパイラと異なり、不完全な情報から即座に有用な応答を返す必要がある。初期化時のインデックス作成、仮想ファイルシステム、ソース世代管理、並列クエリ処理、キャンセル処理など、一見簡単そうな問題が実は深い技術的課題を含んでいることを、rust-analyzerとの比較を交えて説明する。
LSPの構築は、ユーザー体験を台無しにする病的なケースにすべてがかかっている。それもLSPが難しい理由の一つだ。
HNでの議論
30- Panzerschrek
この記事で説明されていることの多くは、Rustだけでなくほとんどどんな言語にも当てはまる。たとえば、リクエストは非同期で処理すべきだとか、UTF-16との相互変換が必要だとかは明らかだ。でも、実際はそれほど難しくない。
私は自分の言語用の言語サーバーも書いた。一番難しかったのは、文書が編集された状態で、文法的に正しくないときに、有用な自動補完を提供する方法を見つけることだった。必要なコンテキストをすべて失わずに、そうした不正さに対処するのが最も厄介な部分だ。
- mitxela
Windowsなら直接の関数呼び出し(COM)になるようなことや、EclipseならJavaモジュール間の直接の関数呼び出しになるようなことに、JSONのTCP接続を使うのはいつも少し気持ち悪く感じられた。
- klodolph
Rustの特性がこれを少し難しくしていると思う。言語をより簡潔にすることと、有用な冗長性を加えることの間にはある種の緊張関係があり、Rustは概して「簡潔」な側に傾いていて、いくつかの冗長性がツールを少し厄介にしている。たとえばインポートだ。
impl std::fmt::Display for Blah {
}
もし言語が(上記のように)インポートを修飾することを強制するなら、LSPは嬉しいことに、プロジェクトの一部がパースできていなくても、リネームのような特定の操作を確実に実行できる。しかし、std::fmtをグロブインポートし、さらに別の何かをグロブインポートすると、お手上げだ。Displayがどこから来るのかわからない(おそらく現時点でパースエラーがあるモジュールからかもしれない)。グロブインポート(または同等のもの)が完全に禁止されているか、典型的なコードで使われない言語は本当にありがたい。
一方、新しいファイルを追加するときには、こんな小さなダンスがある。
mod mycoolmod;
と言って、それからmycoolmod.rsを作成する。あるいは逆の順序でやる。ちょっとした冗長性(ファイルが存在し、それが宣言されている)は、LSPにちょっとした摩擦を生むだけのように思える。なぜなら、mycoolmodは親で宣言されるまで機能するLSPを得られないからだ(両方を作成する必要があり、その後しばらくモジュールが未使用だという一時的な診断が出る)。小さな問題で、Rustのツールにおけるもう一つのちょっとした摩擦に過ぎず、型システムとは何の関係もない。
- octoberfranklin
地獄へようこそ:LSPは自分が真実の源だと想定しているが、それでも自分でファイルシステムにアクセスし、しかも同期した方法で行う必要がある。
LSPはまったく恐ろしい技術設計の一例だ。
MicrosoftにプロトコルやAPIを設計させるのはやめろ。彼らはそれがとてつもなく下手なんだ。