Emacs で LSP を活用してみる

## LSP とは？

[*Language Server Protocol (LSP)*](https://microsoft.github.io/language-server-protocol/) は、言語の解析などを行う *Language Server* と各ツール（クライアント）が通信・連携する際のプロトコルを規定するものです。

<img width="884" alt="language-server-sequence.png" src="https://qiita-image-store.s3.amazonaws.com/0/144707/98a7bd27-09bb-4c0f-b11b-a0f49bd0a859.png">

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

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

参考資料1: [What is the Language Server Protocol?](https://microsoft.github.io/language-server-protocol/overview)
参考資料2: [Qiita/language server protocolについて（前編）](https://qiita.com/atsushieno/items/ce31df9bd88e98eec5c4)

## LSP client package

Emacs では主に2つの package が LSP をサポートしています。
（各エディタのサポートツールは[こちらのページ](https://microsoft.github.io/language-server-protocol/implementors/tools/)、または[こちらのページ](https://langserver.org/)）

#### [lsp-mode](https://github.com/emacs-lsp/lsp-mode/)

[company-lsp](https://github.com/tigersoldier/company-lsp) や [lsp-ui](https://github.com/emacs-lsp/lsp-ui), [imenu](https://www.gnu.org/software/emacs/manual/html_node/emacs/Imenu.html), [xref](https://www.gnu.org/software/emacs/manual/html_node/emacs/Xref.html) 等と連携をして 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](https://github.com/joaotavora/eglot)

`lsp-mode` とは異なるもう一方の package。`xref-find-definitions` や `flymake-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」](https://qiita.com/Ladicle/items/e666e3fb9fae9d807969) などが参考になると思います。
また、今回は Ruby の LSP を元に説明しますが、他の言語でも Language Server の導入以外では特に違いがないと思います。

1. `eglot` の導入

```elisp
(require 'eglot)

;; eglot を ON にする mode を指定
(add-hook 'ruby-mode-hook 'eglot-ensure)
```

2. Language Server の導入

Ruby の場合は [solargraph](https://github.com/castwide/solargraph) が主流のようです。
（各言語の Language Server 対応表は[こちらのページ](https://microsoft.github.io/language-server-protocol/implementors/servers/)）

```
$ gem install solargraph
```

---

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

<img width="840" alt="Screen Shot 2019-01-12 at 21.12.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/144707/5ebba876-2047-75b8-1db9-256918b777ac.png">

## eglot 使用例

### Code Completion

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

![completion.gif](https://qiita-image-store.s3.amazonaws.com/0/144707/24ab9463-e565-fe9d-a3b5-9dd6c3be2d15.gif)

### Go to Definition

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

![definition.gif](https://qiita-image-store.s3.amazonaws.com/0/144707/57750774-ccbb-1e46-dbf7-41f24170ba1d.gif)

ちなみに [smart-jump.el](https://github.com/jojojames/smart-jump) を用いることで、定義参照に `xref-find-definitions` を呼び出しつつ [dumb-jump.el](https://github.com/jacktasia/dumb-jump) を fallback として使用することができるので、おすすめです。

```elisp
(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` でシンボル箇所の詳細説明を見ることができます。

![references.gif](https://qiita-image-store.s3.amazonaws.com/0/144707/43de54e4-24cd-1923-1143-9be1314a0f3a.gif)

普段は [counsel-rg](https://github.com/abo-abo/swiper) を `find-references` 代わりに使っているのですが、 `eglot` 有効時には `xref-find-references` を実行させるようにしてます。

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

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

## 最後に

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

## 参考資料

+ [Language Server Protocol](https://microsoft.github.io/language-server-protocol/)
+ [Languageserver.org](https://langserver.org/)
+ [GitHub/lsp-mode](https://github.com/emacs-lsp/lsp-mode)
+ [GitHub/eglot](https://github.com/joaotavora/eglot)
+ [Qiita/language server protocolについて（前編）](https://qiita.com/atsushieno/items/ce31df9bd88e98eec5c4)
+ [Qiita/好みのエディタに快適な開発環境を提供するLSP](https://qiita.com/Ladicle/items/e666e3fb9fae9d807969)