Emacs 29の eglotを使った Rust環境の設定

Emacs 29から eglotが標準パッケージに入るということで, 最近はちょっとした C++や Goを書くときなどは Emacsを使うことが少し増えてきた. 仕事だと IDEVScodeだけど. そこで Rustも設定しておくかということでやってみた. VSCodeだと基本的に全部自動だが, Emacsでの作業が幾分必要であるのはやはり面倒だと思った.

必要なツールのインストール

% rustup component add rust-analyzer
% rustup component add rust-src 

PATHの設定

rust-analyzer を PATHに設定されている場所に置く. 直接 PATH設定するなり好きにすればよい

% ln -s ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/rust-analyzer ~/.cargo/bin/ 

Emacsの設定

;; rust-modeのインストール. 使っているパッケージに合わせてください. 下記は el-get
(el-get-bundle rust-lang/rust-mode)

;; 補完のために company-modeも入れる
(el-get-bundle company-mode/company-mode :name company-mode)

;; elgotの有効化
(add-hook 'rust-mode-hook 'elgot-ensure)

単純な Rustプロジェクトであればこれだけで動くが, 複数プロジェクト構成だったり, git rootに Cargo.tomlがないケースではうまく行かない問題があった. rust-analyzerは性質上 Rust project rootで起動する必要があるようだが, eglotは project.elの機能を使って project rootを探しており, それはデフォルトでは .gitがあるディレクトリになってしまう. このため .gitがあるディレクトリと Cargo.tomlがあるディレクトリが一致しない場合, LSP用のサーバが起動できずエラーになってしまう. そこで project rootを探す処理に手を入れ対応する必要がある. 具体的には project.elは project-find-functions に登録されている関数を順番に実行し, non-nilが返った場合 それを project rootとして認識するので下記のような対応を行った.

(defun my/find-rust-project-root (dir)                                                                           
   (when-let ((root (locate-dominating-file dir "Cargo.toml")))                                                         
     (list 'vc 'Git root)))

(defun my/rust-mode-hook ()
  (setq-local project-find-functions (list #'my/find-rust-project-root)))

(add-hook 'rust-mode-hook #'my/rust-mode-hook)

これで .git rootに Cargo.tomlがないようなケースでも動くようになった.

おわりに

VSCodeとか IDE使っていたら自動ですべてやってくれるから LSPの恩恵というのをイマイチ実感していなかったけど, LSPはかなりライフチェンジングなものなんだなと Emacsの設定を通じて今更感じた。VSCodeとかに比べればまだまだ手間なんだけど、Emacs29からは上記のような特殊なケースは除くとしてもサーバプログラムさえあれば LSPの恩恵を数行の設定で受けられるってのはとても良い体験だと感じた。