LSP時代のScala開発環境: Metals, Bloop (on Emacs / lsp-mode)

これまでScalaでの開発にはENSIMEを使ってきたけど, もうそろそろ頃合いだとおもうのでMetalsに乗り換えた. エディタ側でLSPのサポートが充実してきているのでこれはだいぶ簡単で, さっくり乗り換えることができた.

Metalsはビルド部分は裏側でBloopを使っているので, テストの実行なんかもこれに乗っかるとだいぶ楽になる. けどEmacsからBloopを利用するにはまだちょっと面倒なところもあったので, この際いろいろ整備してみた.

https://cdn-ak.f.st-hatena.com/images/fotolife/t/tarao/20190627/20190627211424.png

Metals + Bloop

MetalsはまぁふつうにScalaのlanguage serverという感じだけど, ENSIMEと比べると以下の点が強力(個人的な視点).

  • コンパイラがホンモノなのでエラーを誤検知しない
    (従来の開発環境では)
    • presentation compilerと呼ばれる, ちょっとインチキしてるやつだった
    • 誤検知で真っ赤になって困っていた
  • SemanticDBを使っていてソースコードの情報を高速に扱える
    • ENSIMEもlucene使ってたりして頑張ってたけどね...
  • ビルドまわりはBloopを使っているので他との連携がスムーズ
    • エディタにハイライトするためにコンパイルが終わってれば結果を流用できる
      • つまりをテストを実行したりREPLを開くときにコンパイルを待たなくてよい!
    • 自分でビルド情報が必要なツール作りたくなっても比較的かんたん

Metalsでできること・できないこと

(個人的な視点で欲しかったものベースなので網羅性はない)

できる
  • 型情報やコンパイルエラーの情報の表示
  • ドキュメントやメソッドパラメータの表示
  • 定義へのジャンプ
  • 補完
  • シンボルの利用箇所の列挙
できない

激しく必要な機能は揃っていて, 残りもissue化されていてやる気はありそうなので今後に期待!

Emacsで使う

基本的には公式の設定方法に従ったらよいだけ. とはいえ面倒な部分もあったので自分でいろいろ整えた.

MetalsとBloopをインストールするのすら面倒

どこにインストールするかとか考えたくもない. バージョンアップするときどのファイルを置き換えたらいいかも考えたくない. そこで, Emacs側で設定してあれば裏で勝手にダウンロードしてきてBloopサーバも起動しといてくれるようにするやつを作った.


(require 'scala-bootstrap)
(require 'lsp-mode)

(add-hook 'scala-mode-hook
          '(lambda ()
             (scala-bootstrap:with-metals-installed
              (scala-bootstrap:with-bloop-server-started
               (lsp)))))

という感じにするとscala-modeでいますぐMetals生活が始められる(実際に使ってる設定はこういうかんじ). 最新版にするときはM-x scala-bootstrap:reinstalll-metalsとかでイケる. カスタム変数を設定すればバージョン固定もできる.

Bloopを使うのが面倒

コマンドラインbloop test --only \*MySpec myprojectみたいにするのはめんどくさすぎる. プロジェクト内のファイルを編集中になにかてきとうなコマンドを叩いたらさっと実行してほしい. sbt-modeではそういうのができた.

調べてみるとemacs-bloopというのがあるけど, だいぶ雑でrequireできるようにすらなってないし, bloop consoleには対応していなかった. ということでforkして足りないところを補った.

README.mdまでちゃんとする余力はなかったのでこのへんの設定から使い方を感じとってほしい.

がんばったところとしてはbloop consolecomint-modeでうまく扱えるようにいろいろしてるところ. 具体的には以下のあたり.

  • 色指定以外のANSIエスケープコード(CSI)もやってくるので無視する
  • プロンプトになぜかスペースが一つ余計につくので除く
  • 複数行入力を適切に扱う
  • エコーバックされてくると二重に出ちゃうのでなんとかする

副作用としてプロンプトをEmacs側で任意にカスタマイズできるようになった.

感想

めちゃくちゃ快適. 定義にジャンプするときとか早い気がするし, 都度都度コンパイルされ続けているので改めてコンパイル待ちする機会はほぼ皆無. ブランチ切り替えて依存が変わったときとかも, インポートしなおしのフローがなんかわかりやすい. lsp-uiよくできてる. もう戻れない.

追記 2019-06-29

ScalaMatsuri 2019のアンカンファレンスで「ScalaEmacs + Ensimeで開発するノウハウを教えてほしい」というのがあったので, 「いや, そこはもうMetalsを使っていきましょう」という話をさせてもらった. EmacsScalaを書いていると珍しがられて「物好き」のレッテルを貼られてしまうけど, 仲間が10人くらいはいた. このまま, EmacsScala書くのぜんぜんいけるけど? という感じを出していきたい.