症状
Emacsで .go ファイルを開くと以下のログが無限ループする。
[eglot] Asking EGLOT (mydns/(go-ts-mode go-mod-ts-mode)) politely to terminate
[jsonrpc] Server exited with status 2
[eglot] Reconnected!
[eglot] Connected! Server 'gopls' now managing '(go-ts-mode go-mod-ts-mode)' buffers in project 'mydns'.
[jsonrpc] (warning) Sentinel for EGLOT (...) still hasn't run, deleting it!
[jsonrpc] Server exited with status 9
[eglot] Reconnected! [2 times]
Error running timer: (error "Selecting deleted buffer")
status 9 は SIGKILL。gopls が起動→即死→reconnect を繰り返し、補完が一切効かない状態。
fmt. と打っても候補が出ない、もしくは関係のないゴミ候補が出る。手動で M-x completion-at-point を叩いても No match。
調査
eglot-events-bufferで何も見えない
まず M-x eglot-events-buffer でgoplsとのやり取りを確認しようとしたが、何も表示されなかった。
設定を確認すると原因がわかった。
(setq eglot-events-buffer-config '(:size 0 :format short))
:size 0 でイベントバッファを無効化していた。デバッグのためにまず :size 100 以上に変更する必要がある。
completion-at-pointでNo match
M-x completion-at-point を手動で叩いても No match。goplsまでリクエストが届いていないことが確定した。
eglot-reconnectでループ開始
M-x eglot-reconnect を試したところ、上記のループが始まった。
goplsバイナリ自体は正常
goplsが壊れている可能性を疑った。
which gopls
# /home/wasu/go/bin/gopls
gopls version
# golang.org/x/tools/gopls v0.21.1
バイナリは正常にインストールされていた。
デーモンモードの調査
goplsのログに以下が出ていた。
serve.go:173: Gopls LSP daemon: listening on tcp network, address :12345...
デーモンモードで動いているgoplsとeglotが競合している可能性を疑った。ss -atn | grep 12345 で確認したが該当なし。デーモンは関係なかった。
pkill -9 goplsは効かなかった
プロセスが残骸として残っている可能性を疑い pkill -9 gopls を試したが、ループは継続した。
設定ファイルの確認
lsp.elを確認したところ、eglot-managed-mode-hook に刺さっているパッケージが複数あった。
eglot-xeglot-tempeleldoc-boxeglot-signature-eldoc-talkativeflymake-collectioncape
まず eglot-x を疑った。eglotの内部フックを書き換えるパッケージで、壊れると症状がわかりにくい。:disabled t にして package-delete で削除したが、ループは継続した。
素のeglotに削る
lsp.elをeglotだけの最小構成に削った。
(use-package eglot
:config
(setq eglot-events-buffer-config '(:size 100 :format full)
eglot-send-changes-idle-time 1.0)
(add-to-list 'eglot-server-programs
'((go-ts-mode go-mod-ts-mode) . ("gopls" "-remote=auto"))))
補完が動いた。 eglot周辺のパッケージが原因と確定。
一個ずつ戻す
以下の順で追加して都度 fmt. で補完を確認した。
flymake→ okeglot-tempel→ okeldoc-box→ okeglot-signature-eldoc-talkative→ okconsult-eglot→ okjsonrpc→ okflymake-collection→ okcape→ okeglot-x→ ok
全部戻しても動いた。
結果
犯人を特定できなかった。
推定原因1
Lsp関連のパッケージをすべて消して、一つずつ追加していったことによって、パッケージが更新されたこと要因かもしれないと見ている。
実際には更新されず、インストール済みのパッケージファイルを見ている場合はその限りではない?しかし現実問題動いたので混乱している。
推定原因2
明示的に更新したのは eglot-x を一度 package-delete で削除し、:vc で再取得していた。
(use-package eglot-x
:straight nil
:vc ( :fetcher github :repo "nemethf/eglot-x")
:after eglot
:config
(eglot-x-setup))
なお、eglot-xの再取得は全パッケージを削除する前に行っていた。
つまり「一個ずつ戻す」作業の中でeglot-xが更新されたわけではない。
推定原因2も確度は低い。結局原因不明のまま直った。
まとめ
| やったこと | 結果 |
|---|---|
eglot-events-buffer で確認 |
:size 0 で無効化されていて何も見えなかった |
completion-at-point |
No match、goplsまで届いていない |
| goplsバイナリ確認 | 正常 |
| デーモンモード調査 | 関係なかった |
pkill -9 gopls |
効かなかった |
| 素のeglotに削る | 補完が動いた |
| パッケージを一個ずつ戻す | 全部okだった=いずれかの古いパッケージが更新された?(推定原因1) |
eglot-x を再取得済み |
以降ループが止まった(推定原因2) |
eglotのフックに刺さるパッケージが壊れると症状がわかりにくい。補完が死んだらまず素のeglotに戻して二分探索するのが有効だった。
こんなことで数時間溶かすことになるとは。