Vim
Scala
PlayFramework
sbt

scala-sbtプロジェクト用のvimコンパイラプラグインを作成

このプラグインで出来ること

  • vimでコンパイルエラーが出ている場所を順にジャンプ出来る

導入方法

deinとかNeoBundleを使っている場合は、いつも通り。
プラグインマネージャーとかを特に使用していない場合は、

git clone https://github.com/daikitigogo/vim-scala-sbt-compiler

として、クローンしたディレクトリをVIMのRUNTIMEPATHに追加する。

~/.vimrc
set runtimepath+=クローンしたディレクトリ

設定

基本的にファイルタイプごとにvimプラグインを設定する場合は拡張子で判断するが、sbtでは決まったディレクトリ配下のファイルはすべて同じファイルタイプとしたい。
なので、.vimrcにsbtプロジェクトのルートパスを設定し、その配下のファイルに.scala-sbt-compilerというファイルタイプを追加するようにしている。
※vimでは.区切りで複数ファイルタイプを付加出来る。

以下、設定例。

~/.vimrc
let g:ssc_application_settings = {
\   "sample1" : {
\       "rootPath" : $HOME."/workspace/sample1",
\   },
\   "sample2" : {
\       "rootPath" : $HOME."/workspace/sample2",
\   },
\   "sample3" : {
\       "rootPath" : $HOME."/workspace/sample3",
\   }
\}

キーマップの設定

4つほどキーマップを用意。

~/.vimrc
nmap [お好きなキー] <Plug>ScalaSbtCompilerMake
nmap [お好きなキー] <Plug>ScalaSbtCompilerShow
nmap [お好きなキー] <Plug>ScalaSbtCompilerNext
nmap [お好きなキー] <Plug>ScalaSbtCompilerSet

使い方

ScalaSbtCompilerMake

.vimrcに設定したルートディレクトリ上で「sbt compile」コマンドが実行される。
で、コンパイル結果をvimが解析し、コンパイルエラー箇所を順番にジャンプしていくことが可能となる。

  • vim上のカレントがルートディレクトリにある場合は、「:make compile」とコマンドを実行しても同じ
  • ScalaSbtCompilerMakeを実行した場合は、ルートに移動してから「:make compile」コマンドを実行、元のディレクトリに戻るということをしているだけ

ScalaSbtCompilerShow

エラーの詳細が見れる。

type mismatch;
 found   : Int(2)
 required: String
    val samp1 = new Sample(2)
  • vimの標準コマンドの「:cc」とほぼ同じだが、「:cc」の場合は改行含みのメッセージも1行で表示されてしまう
  • ScalaSbtCompilerShowから実行した場合は、複数行で表示されるようにしただけ

ScalaSbtCompilerNext

次のコンパイルエラー箇所にジャンプする。

  • これもほぼvim標準の「:cn」コマンドを実行しているだけ
  • ただし、ScalaSbtCompilerShowを正しく機能させるためにはScalaSbtCompilerNextを使う必要がある

ScalaSbtCompilerSet

sbtのルートディレクトリにあるquickFixFileというファイルをコンパイル実行結果として取得する。
これはsbtの継続的ビルドと一緒に使う想定。ターミナルから以下のようにコマンドを実行する。

sbt -Dsbt.log.noformat=true ~compile | tee quickFixFile

これでファイルが変更されたら自動的にコンパイルされるようになり、コンパイル結果はコンソールとquickFixFileというファイルに出力される。
ファイルを編集しながら現在のコンパイル状況を確認したくなった場合、ScalaSbtCompilerSetを実行すれば良い。
※「-Dsbt.log.noformat=true」は出力の色付けをなくすオプション(これを指定しとかないと端末制御コードも一緒にファイル出力されてしまう)

と、まあ色々と書いたものの、ほとんどvim標準コマンドを使いやすいようにラップしているだけ。
単純にvimコンパイラプラグインを作成する場合は、「makeprg」オプションと「errorformat」オプションを設定したvimファイルを、VIMRUNTIMEPATHのcompilerディレクトリ内に置けば良い。

makeprgオプションとは

例えば以下のように設定している場合、

sbt.vim
set makeprg=scalac

vimから「make hoge.scala」を実行すると「scalac hoge.scala」が実行される。
単純に「make」が「makeprg」に設定した内容に置き換わるだけ。

errorformatオプションとは

これが重要であり難解。今回設定した内容は以下のようなもの。

sbt.vim
set errorformat=%A[%t%.%#]\ %f:%l:%c:\ %m,
            \%Z[error]\ %.%#errors\ found,
            \%Z[warn]\ %.%#warning\ found,
            \%C[%.%#]\ %m
  • %A以降に指定されたパターンが見つかった場合は複数行エラーの開始となる
  • %Z以降に指定されたパターンが見つかった場合は複数行エラーの終了となる
  • %C以降に指定されたパターンが見つかった場合は複数行エラーの継続となる
  • %tはマッチ内容をエラータイプとして取得する(1文字限定マッチ)
  • %fはマッチ内容をファイル名として取得する
  • %lはマッチ内容を行番号として取得する
  • %cはマッチ内容を列番号として取得する
  • %mはマッチ内容をエラーメッセージとして取得する
  • %.%#は一般的な正規表現でいう .*

以下、sbtのコンソール出力例

 1 [info] Loading project definition from ~/workspace/vim-make- test/sbt/project
 2 [info] Loading settings from build.sbt ...
 3 [info] Set current project to My-Item-Search (in build file:~/workspace/vim-make-test/sbt/)
 4 [info] Executing in batch mode. For better performance use sbt's shell
 5 [info] Compiling 1 Scala source to ~/workspace/vim-make-test/sbt/target/scala-2.12/classes ...
 6 [error] ~/workspace/vim-make-test/sbt/src/main/scala/sample/Main.scala:7:28: type mismatch;
 7 [error]  found   : Int(2)
 8 [error]  required: String
 9 [error]     val samp1 = new Sample(2)
10 [error]                            ^
11 [error] ~/workspace/vim-make-test/sbt/src/main/scala/sample/Main.scala:8:17: not found: value Sample
12 [error]     val samp2 = Sample("Scala")
13 [error]                 ^
14 [warn] ~/workspace/vim-make-test/sbt/src/main/scala/sample/Main.scala:3:13: Unused import
15 [warn] import test._
16 [warn]             ^
17 [warn] one warning found
18 [error] two errors found
19 [error] (compile:compileIncremental) Compilation failed
20 [error] Total time: 2 s, completed 2017/12/17 0:07:21

6行目をerrorformatと比較すると

  • []の中の先頭1文字がエラータイプとして取得されるので、エラータイプは"e"となる
  • 半角スペース〜コロンまでの間がファイル名として取得されるので、ファイル名は"~/workspace/vim-make-test/sbt/src/main/scala/sample/Main.scala"となる
  • ↑のコロン〜次のコロンまでが行番号として取得されるので、行番号は7となる
  • ↑のコロン〜次のコロンまでが列番号として取得されるので、列番号は28となる
  • ↑のコロンの後、半角スペースを挟んで以降がエラーメッセージとして取得されるので、エラーメッセージは"type mismatch;"となる
  • %Aのパターンとマッチしたので、複数行エラーメッセージの開始と判断される

7〜10行目は

  • %Aや%Zのパターンとはマッチせず%Cのパターンとマッチするので、複数行エラーの継続と判断される
  • 6行目で設定されたエラーメッセージに、半角スペース以降の文字が追加されていく

11行目は

  • %Aのパターンとマッチするので、新しいエラーメッセージの開始と判断される
  • 新しいエラーメッセージが開始されると、6行目で設定されたエラーメッセージは自動的に終了となる模様

17行目は

  • %Zのパターンとマッチするため、複数行エラーの終了となる

このように設定方法は非常に分かりにくいが、これを正しく設定していれば、「:cfile 任意のファイル」コマンドで任意のファイルを解析&quickfixfileに設定することが出来る。

github

https://github.com/daikitigogo/vim-scala-sbt-compiler