VSCodeのMetalsプラグインでScala(sbt)環境をIDEっぽくする


# やること

- Scala用のビルドツールsbtを入れる
- VSCodeでIDEっぽくするためにMetalsプラグインを入れる

# あらかじめ用意するもの

- JDK （versionは8か11が推奨、筆者は[AdoptOpenJDK](https://adoptopenjdk.net/)の11を入れています）
- Scala
- VSCode

# sbtの導入

### [sbt](https://www.scala-sbt.org/)とは

Scala用のコマンドラインビルドツールです。一応、GradleやMavenでもScalaのコンパイルを行うプラグインは提供されていますが、sbtはLightbend社（Scalaを提供している会社）に所属するエンジニアがScalaコアチームと連携しながら精力的に開発しているツールらしいです。

### インストール

[こちらの公式DLサイト](https://www.scala-sbt.org/download.html)からダウンロードできます。

- Windowsでインストール
  上記のDLサイトにzip、tgz、msiインストーラが置いてあります。

- MacOSでインストール
  Homebrewを使って`brew install sbt`を実行するのが一番早いです。


ちなみに、sbtの導入についてはScalaの公式ドキュメントでも紹介されています。
 → [コマンドラインの sbt で Scala を始める](
https://docs.scala-lang.org/ja/getting-started/sbt-track/getting-started-with-scala-and-sbt-on-the-command-line.html)

### sbtシェルの起動

```sh
$ sbt
```

サブコマンドを指定せずにsbtコマンドを実行すると、依存ライブラリの読み込み等の処理が走った後にsbtシェルが立ち上がります。

適当な場所でsbtコマンドを打った例です。

```sh
$ sbt
[warn] No sbt.version set in project/build.properties, base directory: /path/to/sample/sample-project
[info] Set current project to sample (in build file:/path/to/sample/sample-project/)
[info] sbt server started at local:///path/to/sample/.sbt/1.0/server/6b8ffa8632ff85c4981e/sock
sbt:sample-project> 
```


初回のsbtコマンドで自動的にいくつかのディレクトリとファイルの構造が出来上がりますが、加えてソースファイルの置き場所等を用意する必要があります。詳しくは[こちら](https://www.scala-sbt.org/1.x/docs/Directories.html#Directory+structure)。

```sh
├── build.sbt # プロジェクト名や依存ライブラリなどのビルドの設定をここに書きます
├── project
│   ├── build.properties
│   ├── plugins.sbt # 外部プラグインをここで設定します
│   ├── project
│   └── target
├── src
│   ├── main
│   └── test
└── target
```

build.sbtがある場所でsbtコマンドを実行すると、build.sbtに記述された設定通りのsbt環境が出来上がります。

```scala:build.sbt
lazy val root = (project in file(".")).settings(
  name := "sample-project",
  version := "0.1",
  scalaVersion := "2.13.4", // 使用するScalaのバージョンを書く
  // 依存する外部ライブラリ
  libraryDependencies ++= Seq("org.scalatest" %% "scalatest" % "3.1.1" % Test)
)
```


### sbtコマンドざっくり紹介

|sbtコマンド|sbtシェル起動時|説明|
|---|---|---|
|`sbt run`|`run`|mainクラスを実行します。|
|-|`exit`|sbtシェルを終了します。|
|`sbt console`|`console`|Scala REPLを立ち上げます。|
|`sbt reload`|`reload`|ビルド定義を再読み込みします。build.sbtを変更した後に使います。|
|`sbt compile`|`compile`|全てのソースファイルをコンパイルします。|
|`sbt ~compile`|`~compile`|ソースファイル監視モードになります（この状態の時は他のsbtコマンドは使えません）。この状態でソースファイルを変更すると、変更ファイルだけにコンパイルがかかります。エンターキーを入力することで監視モードからもとに戻ります。|
|`sbt test`|`test`|テストを実行します。|
|-|`exit`|sbtシェルモードを終了します。|

`sbt ~compile`で動的な差分コンパイルする機能は、次節で導入するMetalsプラグインがあればわざわざ使うことはなくなると思います。

### gitignore

sbt環境ではこれを書いておけば大丈夫です。

```sh:.gitignore
target/
```

# Metalsの導入

### [Metals](https://scalameta.org/metals/)とは

Java/Scala環境をIDEっぽくしてくれるプラグインです。今回はVSCodeとsbtの組み合わせを紹介しますが、Metalsは複数のテキストエディタと複数のビルドツールに対応しています。

- [対応しているエディタの表](https://scalameta.org/metals/docs/editors/overview.html#editor-support)
- [対応しているビルドツールの表](https://scalameta.org/metals/docs/build-tools/overview.html)

自分はよく知りませんが、内部でBloopというScala用のビルドサーバーが動いているようです。


### インストール

VSCodeから[Metalsのプラグイン](https://marketplace.visualstudio.com/items?itemName=scalameta.metals)をただ追加するだけです。VSCodeのマーケットプレイスで「Scala」と打ったら上から２〜３番目くらいに出てきますので追加してください。依存プラグインは[Scala Syntax (official)](https://marketplace.visualstudio.com/items?itemName=scala-lang.scala)だけです。


すでに[Scala (sbt)](https://marketplace.visualstudio.com/items?itemName=lightbend.vscode-sbt-scala)を入れている場合は無効にした方が良いらしいです。自分は途中まで気付かずに有効のままでした。[Scala Language Server](https://marketplace.visualstudio.com/items?itemName=dragos.scala-lsp)というそれっぽいプラグインもありますが今回は入れる必要ないです。


私の環境では上2つだけが有効の状態です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/946419/27c5db83-93ae-4b69-3a73-b41de8f1cf15.png" width="400"/>


### 簡単な確認

プラグインが入ったらsbtプロジェクトのルートディレクトリでVSCodeを開くだけで終了なのですが、[一応環境を用意してみました。](https://github.com/Yoshifumi14/HelloMetals)適当なソースファイルを用意したりするのが面倒な方はこちらからクローンしてコード補完などの雰囲気だけでもお試しください。

```
$ git clone https://github.com/Yoshifumi14/HelloMetals.git
```

クローンしたら、VSCodeでそのプロジェクトを開きます。（[codeコマンドの使用方法](https://qiita.com/naru0504/items/c2ed8869ffbf7682cf5c))

```
$ code HelloMetals
```

すでにsbtプロジェクトがあるので、Metalsがそれを検知していろいろやってくれます。初回は時間かかかりますが、Metalsの読み込みが終わったらそれで完了です。

加えてsbtコマンドも実行できますが、初回は時間がかかるので別にやる必要はないです。

### コード整形について

Metalsはコード整形も自動でやってくれます。内部で勝手に[Scalafmt](https://scalameta.org/scalafmt/)というScala用のコード整形ツール（提供元はMetalsと同じです）を使用しており、開いているルートディレクトリ直下に.scalafmt.confファイルが勝手に作られるので、さらに設定を追加したい場合はここに書きます。

一応、Scalafmtはsbt単体のプラグインとしても用意されていますが、Metalsも一緒に使用するならばプラグイン設定を記述するplugings.sbtへの記述は不要です。

### gitignore

Metals関連はこれを追記します。

```sh:.gitignore
.metals/
.bloop/
.ammonite/
metals.sbt
```

### プラグインのGUI

自分はあんまり使ったことがなくて申し訳ないのですが、左側のサイドバーから開けるMetalsのGUI操作でいろいろできそうです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/946419/0e8f3972-4bc4-9786-3d9e-44c7b3e37298.png"/ width="250">
[New Scala project]でsbtやmeven用のプロジェクトをテンプレートから作成できました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/946419/71d8d8d8-6a06-0d9c-1dee-63f40edf4cda.png"/ width="500">

# 気をつけること

Metalsが要求するScalaのバージョンがちょっと高いので困る時があります。

私の例では、ちょうど1年前くらいにScala 2.13.1で作った環境があったのですが、今その環境をVSCodeで開くと「2.13.1は将来的にサポートしないかもしれないから2.13.4に更新してくれ」と言われてしまいました。

Scalaのバージョンを更新するには、使用している依存ライブラリが更新後のバージョンでも動くかをよく調査してからでないといけません。なので安易にバージョンを上げることができない点に注意が必要です。

バージョンについては[公式ドキュメント](https://scalameta.org/metals/docs/editors/vscode.html#requirements)で以下のように説明されています（2021年1月時点）
>Scala 2.13, 2.12, 2.11 and Scala 3. Metals supports these Scala versions 2.13.4, 2.12.12, 2.12.11, 2.12.10, 2.13.2, 2.13.3, 2.11.12, 2.12.8, 2.12.9, 2.13.0, 2.13.1, 3.0.0-M3, 3.0.0-M2, 3.0.0-M1 and 0.27.0-RC1. Note that 2.11.x support is deprecated and it will be removed in future releases. It's recommended to upgrade to Scala 2.12 or Scala 2.13

# 終わり

開発環境系の話は試す人によっては思わぬところでハマることがどうしても多いので、うまくいかない可能性も十分あります。なので、せめて必要な具材や調理法の外観だけでも共有できたら幸いです。

また、今回は開発環境についてでしたが、気が向いたら続編ということでビルドやデプロイ周りのことを書いてみようかと思います。sbtやDockerの話が中心となる気がします。

では、ありがとうございました。

# 参考文献

- Scala公式: https://docs.scala-lang.org
- sbt公式: https://www.scala-sbt.org
- Metals公式: https://scalameta.org/metals
- Bloop公式: https://scalacenter.github.io/bloop
- scalafmt公式: https://scalameta.org/scalafmt
- 実践Scala入門: https://www.amazon.co.jp/dp/4297101416