• 作成:

EmacsでJava開発をする時eglotではなくlsp-modeを使えば割とあっさり構築完了しました

ぐだぐだ試行錯誤したログを載せています.

EmacsでJava開発をする

専用IDE以外でJava開発をするのは苦行であることが知られています.

実際私も2015年ごろに学校の課題で ncaq/chat.sd: チャットツールを開発していた時はflycheckが対応していなくて一々コンパイルエラーをgradleで確認する地獄の作業を行っていました. それでもクソ遅い上に頻繁に壊れるEclipseよりはマシだったので皆よりは優位を取っていた気がします. 正直あの環境だったらIntelliJを学割入手した方がマシだった気がしますが意地貼ってEmacs使っていました.

しかし今はVSCodeの影響でLSPの勢いが強いのでテキストエディタに優しくなったはずなのでEmacsで再突入しようとしています.

Emacs以外使うと生産性激落ちするのでやっぱり離れたくない.

ENSIMEの死

ENSIMEはScala用の開発ツールでLSPにも対応していました. 私もかつてflycheckと接続できるツールを開発したりしていました. ncaq/flycheck-ensime: display ensime error message for flycheck buffer.

しかし作者がMetalsの優遇にブチギレてプロジェクトを削除してScalaコミュニティからも引退してしまいました. Home · ENSIME

まあそれ以前にJavaサポートをやめてしまったようですね.

Haskellに移ったようなので本業Haskellerであるはずの私的には, Haskellの開発環境がこれで良くなったら良いなあなどと思います. それはそれとして困りました.

groovy-modeは必須

とりあえずプロジェクトのビルドがgradleで行われているので groovy-mode をインストール.

これでgradleのシンタックスにも対応されます.

Metalsはダメ

ENSIME消失の原因となったmetalsですが, Javaには全然対応していないらしいのでダメみたいですね. Interoperability with Java language servers · Issue #5 · scalameta/metals-feature-requests

meghanadaという選択肢

meghanada というENSIMEに似たツールがありますが, とりあえず最近のものはLSPで合わせたいのでLSPがダメだったらにします.

Langserver.orgによるとLSPの選択肢は2つある

Langserver.org によるとJavaのLSPの選択肢は

の2つがあり, Eclipseに依存しなくてDiagnosticsに対応していない後者を選びたくなる所ですが, 後者はCIのエラー放置してて不安感が強いですね… 後インストールがconfigureするやつなのでこれも相応に面倒そうですね…

Eclipseのインストールに不安が募りますが, 普段使いはせずにLSPのためだけに使うので問題ないでしょう. 多分.

joaotavora/eglot: A client for Language Server Protocol servers がデフォルトサポートしてるのもEclipseの方ですしね.

eclipse.jdt.lsをインストールする

このリポジトリ自体をcloneする必要があったりeclipseをパッケージ管理システムに頼らずダウンロードしてくる必要があって怖すぎる.

Eclipseの設定して

./mvnw clean verify

したら引くほどパッケージがダウンロードされてきて, テストに失敗しました. なんでや.

うまくセットアップ出来ない. これだからJavaは嫌いなんですよ未だにLSPを設定するのすらEclipseとかが必要になってくるしエラー. なんでJavaは開発環境構築がコマンドラインで完結しないんですかね? 謎IDEの謎メニュー開かないといけないらしいけどそんなものはない状態になってるのでキレてます.

If, after importing the projects, you see an error on pom.xml about Tycho, you can use Quick Fix (Ctrl+1) to install the Tycho maven integration.

こんなこと書いてすけけど全くQuick Fixで解決できない…

わかんねえ… 私さあ頭悪いからコマンドラインで何かを実行しろってのならわかるけどGUIで何かを実行しろって言われても難しいんですよ.

「Quick Fixする」という操作を何処で行うか分からなかったけどエラーボックスの項目を右クリックして出てくるQuick Fixを使うのですね. そんな文脈はわからない.

「ターゲットプラットフォームの選択」というのがWindowにあることが何処にも書かれて居なかったからさまよったけど, これは既にEclipseが自動でインストールしていたようですね.

POMファイルの問題を解決しても

Failures:
  GradleProjectImporterTest.testDeleteClasspath:320

で進まないことがわかりましたね. ググったらJDK 8でやれと書いてありますが, Build failure via command line · Issue #1209 · eclipse/eclipse.jdt.ls JDK 8でやってるんですよねえ…

必要ならmavenをインストールしろと書かれていたのでmaven-binをインストールしてみました. ただそれでコケるならパッケージ取得のところでコケるはずで謎のテストでコケるのは納得いかないんですよね. と思ったらこれは誤読でコマンド実行すると自動的にMavenはインストールされるようですね.

gradle関係のテストでエラーが起きているとのことなのでシステムのgradle-binをアンインストールしてみます. 変わらずエラー.

同じJDK 8でもopenjdkではなくIcedTeaを使っているのが悪いのかもしれません. AdoptOpenJDKの8を使ってみましょう. 変わらずエラー.

一度cloneし直すところからやり直してみます. 余計な操作で余計な設定がついてしまったかもしれないので. 変わらずエラー.

エラーログは以下のようになっているみたいですね.

-------------------------------------------------------------------------------
Test set: org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest
-------------------------------------------------------------------------------
Tests run: 16, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 4.853 s <<< FAILURE! - in org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest
testDisableImportGradle(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.061 s
testBuildFile(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.853 s
testJava11Project(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.734 s
testJavaImportExclusions(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.162 s
testGradleHomePreference(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.014 s
testDeleteClasspath(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.238 s  <<< FAILURE!
java.lang.AssertionError
	at org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest.testDeleteClasspath(GradleProjectImporterTest.java:320)

testGradlePropertiesFile(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.281 s
testGradleArguments(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.014 s
testJava12Project(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 1.015 s
testGradleUserHome(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.014 s
testGradlePersistence(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.416 s
testGradleJvmArguments(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.013 s
testDisableGradleWrapper(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.661 s
testWorkspaceSettings(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.013 s
importNestedGradleProject(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.2 s
importSimpleGradleProject(org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporterTest)  Time elapsed: 0.143 s

エラー箇所は

assertFalse(bin.getRawLocation().toFile().exists());

もうわからない. 手順通りにやってるのにエラーが出るのはバグなのでは? バグの疑いがあるのでGitHubのissueに報告しましょう.

Build failure by GradleProjectImporterTest.testDeleteClasspath:320 · Issue #1251 · eclipse/eclipse.jdt.ls

悪魔的対策としてソースコードのこの部分だけ削除してしまってインストールするというものがありますね.

この部分だけ削除したらビルド成功してしまった.

eglotに設定する

デフォルト設定だから大丈夫だろうと適当に開いたら

eglot--managed-mode remove-hook post-command-hook nil buffer-live-p apply eglot--connect eglot--guess-contact] 4]): (error "Could not find eclipse.jdt.ls jar in CLASSPATH")

とエラーが出てきましたね.

?ちょっと見てみましたがよくわかりませんね.

How to configure where eclipse.jdt.ls is located? · Issue #248 · joaotavora/eglot によると単に$CLASSPATHを設定すれば良いらしいですね. しかしこのissueの通りに設定してもうまく行きませんね.

eglot--managed-mode remove-hook post-command-hook nil buffer-live-p apply eglot--connect eglot--guess-contact] 4]): (error "Could not find required eclipse.jdt.ls files (build required?)")

と言われてしまいます.

ソース見てリポジトリを設定する必要があるのかと思ったのですがそれもダメ.

と思ったけど

jarをCLASSPATHに指定して

export CLASSPATH=~/Desktop/eclipse.jdt.ls/org.eclipse.jdt.ls.product/target/repository/plugins/org.eclipse.equinox.launcher_1.5.600.v20191014-2022.jar:$CLASSPATH

Running from the command line

を実行してconfig_linuxを生成すれば大丈夫なようです. ビルド時に生成されないのですね. 一度コマンドラインで実行する必要があります.

eglot諦めてlsp-mode使うことにしました

コードジャンプは出来るようになりましたがリアルタイムエラーチェックが動きませんね.

イベントログバッファを見るとエラーのリストは貰ってるように見えますね.

flymakeのエラーリストを無理やり表示してみたら何故かflycheck側のリストに反映されないだけでエラー自体は認識されてるように見えます.. 何故かJava 11で実行した結果のエラーが表示されますが…

よく考えてみると元々flymakeのものが表示されてると勘違いしてただけで, これまでのLSP有効言語のエラーチェックってflycheckが独自にサポートしてたのですね. Rustの場合はrusticがrlsの内容ではなくclippyを提供していました. 出来ればflymakeではなくてflycheckで閲覧したいのですけど可能なのでしょうか.

色々調べて考えて, eglotじゃなくてlsp-modeなら普通にflycheckが使えるようなので素直に移行することにしました.

(with-eval-after-load 'lsp-mode
  (custom-set-variables
   '(lsp-prefer-flymake nil))              ; flycheckを優先する

  (define-key lsp-mode-map (kbd "C-c C-d") 'lsp-describe-thing-at-point)
  (define-key lsp-mode-map (kbd "C-c C-e") 'lsp-workspace-restart)
  (define-key lsp-mode-map (kbd "C-c C-i") 'lsp-format-buffer)
  (define-key lsp-mode-map (kbd "C-c C-n") 'lsp-rename)
  (define-key lsp-mode-map (kbd "C-c C-r") 'lsp-execute-code-action)
  )

のように設定すればflycheckが使われます.

emacs-lsp/lsp-java: lsp-mode java を使えばlspサーバも自動でダウンロードされてパスの設定も必要ないことが分かったので無の気持ちになっています.

lsp-modeを使えば割とあっさり構築完了して移行にもそんなに手間かかりませんでした…

何故かコンパイル時とエラーチェックが違う

やってるプロジェクトでコマンドラインのgradleからはコンパイル通るのに, lspからだとJava 11でコンパイルした時と同じエラーメッセージが表示されるのが謎です. 本物のコンパイラを使っているので違いが出るとは思えないのですが… 動いているプロセスを見てもIcedTea 8で動いていますし.