読者です 読者をやめる 読者になる 読者になる

たごもりすメモ

コードとかその他の話とか。

EmacsでJava開発をする @ 2017

お仕事でそれなりにまとまったJavaのコードを書くので、ちょっと真面目に手元の環境をアップデートしようと思ったらいろいろあったので、メモを兼ねてまとめる。なお手元の環境のアップデートにともない、次のエントリを大きく参考にさせていただきました。多謝。
qiita.com

なおそれまでの環境は EmacsでJavaを書く - nekop's blog を元にだいぶ前に整備したもので malabar-mode をずっと使ってたんだけど、Java8 Lambdaでインデントが崩れるとかいろいろあってちょっとストレスフルだったのが主なアップデート動機。

環境は OS X El Capitan *1 + Emacs 25.2 で、全面的に MELPA をつかってパッケージ管理やっている。

結論

最終的には ENSIME (の ENJINEモード) を使っている。これでコンパイルエラーを出すとかメソッド名を補完するだとかのエディタ上で最低限欲しい機能はだいたい揃った。ちょいちょい動かない機能とかEmacsを巻き込んでクラッシュする機能とかがあるけど、自分は元々エディタ上で操作を完結させたいとか思わない*2ので、まあこれくらいでいいやと。

その上でコードの自動フォーマットをチーム内で揃えるため、別途コマンドラインツールと git pre-commit hook の組み合わせで自動フォーマッタをかませることにしようとしている。これに使えるものもいろいろ考えたけど、IntelliJ IDEAにコードフォーマッタがあることを知ったのでこれを使うことにした。IntelliJ IDEAはエディタとしては使いたくないけど、コマンドライン起動するためにインストールするくらいはなんでもないぞ。

#!/bin/sh

CURRENT_DIR=$(pwd)
SETTING=$CURRENT_DIR/misc/airlift-codestyle.xml
FORMATTER="/Applications/IntelliJ IDEA CE.app/Contents/bin/format.sh"

if git rev-parse --verify HEAD >/dev/null 2>&1
then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=393d8382981dcc6a4e5e907ad87c55e09d592715
fi
exec 1>&2

# .java files to be committed
files=$(git diff-index --name-status $against -- | grep -E '^[AUM].*\.java$'| cut -c3- | sed -e "s|^|$CURRENT_DIR/|")
echo "$files" | xargs "$FORMATTER" -s $SETTING
echo "$files" | xargs git add

こんな感じのスクリプトを書いて pre-commit hook として使う。設定ファイルを与えられるんだけど、ここではPrestoとかでも使われてるAirliftの設定ファイルを与えることにしている。なぜか。まあ自分にとってもこのスタイルはそんなに悪くない。あんまり主張が強くないし。

問題はあって、なにしろ(Javaのコードを含んだ)commitのたびに裏側で IntelliJ IDEA を起動するので、遅い。あとなんかログとか警告とかも流れてきてちょっと邪魔い。けどcommitの頻度自体そんな高いもんじゃないし、まあいいかなと思っている。好きなエディタ使えるほうが大事。

ENSIMEの環境設定

正直、これはだいぶ大変だった。が、たぶん最適解が分かってればすぐ終わるんじゃないかなという気がする……。たぶん。基本的には Installation · ENSIME に従えばよい。あとプロジェクトが gradle を使っているので Gradle · ENSIME も。

が、いろいろ試したところうまく動かない組合せが多く、唯一動作したのは ENSIME 0.2.8 (stable) と gradle 3.3 の組合せだけだった。unstable (0.3.0-SNAPSHOT) を使うケースだと gradle 3.5 ではバグってて動かず gradle 3.4 では動くように見えるんだけど、Emacs側で melpa から unstable 版を入れて読み込もうとすると .ensime の形式がおかしいとか言われて動かない。
stable版はどうかと ENSIME 0.2.8 を使おうとしたら gradle 2.4+ との組み合わせだとバグってて動かないので gradle 3.3 までダウングレードしたら動いた。これを melpa-stable 経由でインストールした ENSIME 0.2.8 と組合せるとなんとか全体が動作するようになった。

今のところはバッファ開くたびに M-x ensime して有効にしている。しかしなんか同じプロジェクトのクラスを見付けてくれないことがあったり、build.gradleに設定してある依存ライブラリを見付けてくれないことがあるように見える。なんだかなー。まあ、自分にとってはそこまで致命的ではない。

Meghanada

他の選択肢は? というと、いくつか試してはいる。最初に試したのは Meghanada-Mode で、実際にこれはすばらしくセットアップが楽だった。が、最初に試したときは meghanada-mode を有効にしたあとで C-i したらEmacsごと固まる、という状況で、ちょっとこれは……という感じでいったんやめた。
その後に init.el の大掃除をしたあとでもう一度試してみたら動くには動いたんだけど、機能によってはやっぱり何のデバッグメッセージも無しに固まる症状がちょいちょい見られたりして、きびしい。あと手元ではコンパイル時警告が出てる場所の色付けみたいなのも動いてなかったので、機能として無いのか単に何かがおかしくて動いてないのかは知らないが、あまり便利とは言いがたい状態だったのでいったん使用をやめた。

何しろインストールが楽なので、使えれば使いたいんだけどなー、という状況でした。コードの自動フォーマット機能があるのはすばらしいけど、フォーマッタをどうにかして設定できないかなあ、という気もする。
今後に期待。

Google-java-format

エディタはどうにかするとしてコマンドラインフォーマッタはどうか、と思って見付けたのが google-java-format なんだけど、デフォルトのコーディングスタイルがちょっとアレすぎて使うにはアレ……。2スペとか。

なお --aosp というオプションが最近のバージョンで足されてて、これはどうも Android Open Source Projects 用らしいぞ、ということで試してみたら4スペだし、これはこれでいいかなと思いかけたんだけど、やっぱりちょっとクセが強い感じがした。主張つよくバリバリ修正されるんだけど、改行をやたらアグレッシブに入れるし、その割にここ改行しないんだ? みたいな納得いかない部分がちょいちょいあるし、lambda使ったときにインデントがやたら深くなる傾向があるし、ということで、きびしい。
途中まで使いかけたんだけど IntelliJ IDEA のフォーマッタを見付けたのでそっちに行った。

まとめ

がんばるしかないがちょっとつらい。

*1:まだSierraにしてない、したくないよう

*2:ビルドもテスト実行もコンソールに戻ってやっている