Emacs で LSP を活用してみる


LSP とは?

Language Server Protocol (LSP) は、言語の解析などを行う Language Server と各ツール(クライアント)が通信・連携する際のプロトコルを規定するものです。

従来、コード補完や定義参照などの機能は各ツールが独自に、かつ言語ごとに実装してきましたが、それにはかなりの労力が必要でした。しかし LSP を用いることで、各ツールは Language Server がどの言語を取り扱っているかを意識することなく、補完や定義参照を行うことができます。

なので、Emacs ユーザにとって LSP(と Language Server) は「IDE like なコード補完、定義参照、参照検索等を提供してくれるモノ」として捉えられるんじゃないかなと思います。

参考資料1: What is the Language Server Protocol?
参考資料2: Qiita/language server protocolについて(前編)

LSP client package

Emacs では主に2つの package が LSP をサポートしています。
(各エディタのサポートツールはこちらのページ、またはこちらのページ

lsp-mode

company-lsplsp-ui, imenu, xref 等と連携をして IDE like な機能を提供する package です。かなり多機能で、UI に関しても充実してます。以下、原文。

lsp-mode aims to provide IDE-like experience by providing optional integration with the most popular Emacs packages like company, flycheck and projectile

eglot

lsp-mode とは異なるもう一方の package。xref-find-definitionsflymake-mode, eldoc-mode, completion-at-point などと連携して、定義参照や参照検索を実行させます。lsp-mode よりも非常にシンプルになっています。以下、原文。

Eglot is considerably less code and hassle than lsp-mode.el. 
In most cases, there's nothing to configure. It's a minimalist approach focused on user experience and performance

eglot 設定

eglot に注目して設定方法を紹介します。lsp-mode については 「Qiita/好みのエディタに快適な開発環境を提供するLSP」 などが参考になると思います。
また、今回は Ruby の LSP を元に説明しますが、他の言語でも Language Server の導入以外では特に違いがないと思います。

  1. eglot の導入
(require 'eglot)

;; eglot を ON にする mode を指定
(add-hook 'ruby-mode-hook 'eglot-ensure)
  1. Language Server の導入

Ruby の場合は solargraph が主流のようです。
(各言語の Language Server 対応表はこちらのページ

$ gem install solargraph

以上を設定すると、Ruby (Rails) の Project で Mode Line に eglot と表示されます。

eglot 使用例

Code Completion

変数が Array Class ならば to_h が候補としてサジェストされます。
一方、String Class なら to_h は候補に出ず、代わりに to_i などが適切にサジェストされます。

Go to Definition

eglotxref-find-definitions を用いて変数の定義箇所にジャンプすることが可能です。

ちなみに smart-jump.el を用いることで、定義参照に xref-find-definitions を呼び出しつつ dumb-jump.el を fallback として使用することができるので、おすすめです。

(require 'smart-jump)
(smart-jump-setup-default-registers)

;; お好みでどうぞ
(define-key global-map [(super d)] 'smart-jump-go)

Find References

xref-find-references を用いることで、変数の参照箇所を検索できます。
また、eglot-help-at-point でシンボル箇所の詳細説明を見ることができます。

普段は counsel-rgfind-references 代わりに使っているのですが、 eglot 有効時には xref-find-references を実行させるようにしてます。

(global-set-key (kbd "s-f") 'counsel-rg)

;; eglot-mode 時に有効にする
(define-key eglot-mode-map (kbd "s-f") 'xref-find-references)

最後に

Emacs with LSP、夢がありますね。

参考資料