公式にあるIntelliJのHaskellプラグインはIntelliJでHaskell開発をする上で無くてはならないのだけど、まだまだ発展途上で応用的な機能(例えばリファクタリングとか)は殆ど無かったりする。今のところghc-modiを使ったEmacsでいうflymake的なエラーチェックと、選択範囲の型表示ぐらいがあるだけだ。
なので、ここにHaskell style scannerを追加することを通して、プラグインの拡張方法をまとめてみる。
このHaskell style scannerの機能を足すとこんな感じでスタイルエラーを黄色くハイライトしてくれる。
コンフィグにscanを足して、scanバイナリのパスをセットする
Haskell style scannerは
cabal install scan
でインストールすることができる。パッケージ名に倣ってscan
と呼ぶことにしよう。
このscanの使い方は
scan <ファイル名>
で
Move.hs:28:1: too long line (123 chars) Move.hs:38:1: too long line (96 chars) Move.hs:2:7: put blank before ( Move.hs:40:1: missing final newline
という感じに指摘してくれる。オプションを付けることでファイルを修正させることもできる、が、今回は指摘の表示の方を使う。
まずはghc-modiのようにバイナリのパスをコンフィグに設定できるようにしてみる。
コンフィグの画面はplugin/src/org/jetbrains/haskell/config/HaskellConfigurable.kt
で作られており、実際のコンフィグを保持しておく場所はplugin/src/org/jetbrains/haskell/config/HaskellSettings.java
になる。
private val scan = TextFieldWithBrowseButton() // (in createComponent) ... scan.addBrowseFolderListener( "Select scan execurtable", null, null, FileChooserDescriptorFactory.createSingleLocalFileDescriptor()) ... scan.getTextField()!!.getDocument()!!.addDocumentListener(listener) ... addLabeledControl(5, "scan executable", scan) // (in apply) ... state.scanPath = scan.getTextField()!!.getText() // (in reset) ... scan.getTextField()!!.setText(state.scanPath ?: "")
といった感じでテキストボックスを足して、state (HaskellSettingsクラスのオブジェクト)にパスをセットする。
scanを使ってスタイルチェック
実際にセットしたパスを使ってスタイルチェックをするルーチンを作る。 これもghc-modiで使われている方法をほぼパクってできる。
これは本当にシンプルな外部コマンド呼び出しのサンプル。scanは引数のファイルをスキャンしたら標準出力に警告を一行ずつ吐いてすぐに終了するのでコマンドの終了を待ち合わせる必要もない。
これを実際に使うところはplugin/src/org/jetbrains/haskell/external/HaskellExternalAnnotator.kt
にある。
といった感じで新たに関数を定義する。
先のScan.runCommand
を呼び出して、中身をパーズするだけ。既存のghc-modiのものがそのまま流用できるはずだ。
スタイルエラーをエラーっぽく表示するのはいやなのでWarningレベルで表示するようにした。
それを実際にパーズする場所にプラグインする。
override fun doAnnotate(psiFile: PsiFile?): List<ErrorMessage> { val file = psiFile!!.getVirtualFile() if (file == null) { return listOf() } val baseDir = getProjectBaseDir(psiFile) if (baseDir == null) { return listOf() } val resultGhcModi = getResultFromGhcModi(psiFile, baseDir, file) val resultScan = getResultFromScan(psiFile, baseDir, file) val result = resultGhcModi + resultScan return result }
ErrorMessage
のリストを返す2つの関数をconcatして返すとそれをアノテーションに表示してくれる。かんたんだね!
まとめ
これを応用すれば外部コマンドを使ったいろんなツールが簡単に作れそう。 たとえばすぐに思い浮かぶのはHLintをつかうこと、かな。 本当はAlt+Enterのメニューに出てきてワンタッチで修正が入るようになるといいんだけど、それはまた今度。
この機能は
ysnrkdm/haskell-idea-plugin · GitHub
にある。さらにプラグインだけ使いたい人は
https://github.com/ysnrkdm/haskell-idea-plugin/blob/master/plugin/haskell-plugin.zip
をプラグインとしてインストールしてみてください。バグ報告等歓迎です。
追伸
IntelliJのUltimateエディションを買ってしまった。199ドルなり。