CentOS Stream 8, emacs lsp-mode で pyright を使う

# CentOS Stream 8, emacs lsp-mode で pyright を使う

CentOS Stream 8, emacs の lsp-mode で pyright を使う方法です。

## 主な登場人物

- `pyright`: Microsoft製の python language server
    - https://github.com/microsoft/pyright
    - language server, language server protocol についてはこちらの記事をどうぞ。
        - [language server protocolについて (前編)](https://qiita.com/atsushieno/items/ce31df9bd88e98eec5c4)
- `lsp-mode`: language server protocol で language server の機能を使う emacs minor mode。
- `lsp-ui-mode`: lsp-mode の機能にポップアップ表示等のUI機能を付加する emacs minor mode。
- `python-mode`: python 用の emacs major mode。

## 環境

- CentOS Stream 8 x64
- emacs 28.0.50
    - ssh 経由でのvtyで使用
    - [GccEmacs](https://www.emacswiki.org/emacs/GccEmacs)[^gccemacs]
- node.js v16.6.1
- pyright 1.1.163
- lsp-ui 20210820.1331
- lsp-mode 20210825.1531
- lsp-pyright 20210513.1022
- company 20210826.2148
- flycheck 20210825.1804

[^gccemacs]: [scl](https://wiki.centos.org/AdditionalResources/Repositories/SCL)でgcc-toolset-10 をいれてビルドしたもの。

:::note
2021/8 CentOS7からCentOS Stream 8に変更しました。
:::

## `node.js` を入れる

`pyright` は node.js で書かれています。

ここら辺を見ていれます。
https://github.com/nodesource/distributions/blob/master/README.md#enterprise-linux-based-distributions

yum レポジトリ追加したくない人は、この辺りから、rpm ダウンロードして入れましょう。
https://rpm.nodesource.com/pub_16.x/el/7/x86_64/

```shell-session
$ node --version
v16.6.1

$ npm --version
7.21.1
```

## `pyright` を入れる

```shell-session
$ npm -g install pyright
```

```shell-session
$ pyright --version
pyright 1.1.163
```

emacs から起動されるのは `pyright-langserver` です。
`pyright-langserver` は `pyright` とともにインストールされます。


## emacs にパッケージを入れる

lsp-pyright, lsp-mode, lsp-ui, company, flycheck 全て melpa からインストールできます。

`M-x package-install lsp-mode`
...
 
### `lsp-mode`

https://emacs-lsp.github.io/lsp-mode/

emacs で lsp の機能を使うためのパッケージです。Python用ではなく、汎用です。
Pythonで使う場合、`python-mode` のマイナーモードとして使用します。

コード補完には、`company` が推奨されています。
http://company-mode.github.io/

lsp-mode 7.0.1 で `company-lsp` はサポートされなくなりました。 `company` だけで十分です。
https://github.com/emacs-lsp/lsp-mode/blob/master/CHANGELOG.org
> Dropped support for company-lsp, the suggested provider is company-capf.

### `lsp-ui`

`lsp-mode` を補助するパッケージです。

* `lsp-ui-sideline`
    * エラー、flycheck警告がカーソル行付近の右端に表示されるようになります。
* `lsp-ui-peek`
    * 関数定義位置へのジャンプ、関数参照箇所へのジャンプが、"peek" になります。
    * "peek" は、ジャンプ前にジャンプ先をオーバーレイでプレビューできる機能です。
* `lsp-ui-doc`
    * 関数にカーソル置いたときに表示される doc-string 表示がオーバーレイでの表示になります。
    * GUIだと、doc-string の WebKitレンダリングもできるっぽいです。
* `lsp-ui-imenu`
    * emacs の `imenu` を使って、編集中ファイルのクラス、メソッドのツリー階層を表示してくれます。
    * 2020/8時点では、`imenu-list` の方が高機能なので、そっちつかってましたが、そもそも `imenu` をほとんど使っておらず、別パッケージいれるほどでもないので `lsp-ui-imenu` 使います。

### `lsp-pyright`

https://github.com/emacs-lsp/lsp-python-ms

lsp-mode で、`pyright` を使えるようにしてくれるアドオンパッケージです。

### `company`

IntelliSense 的なコード補完を提供するパッケージです。
いろいろなバックエンドパッケージをいれることで、補完が強化されます。

lsp-mode が(というか、language-serverが) company のバックエンドとして動作します。

### `flycheck`

汎用 linter フレームワークです。

https://www.flycheck.org/en/latest/

## emacs をコンフィグする

`use-package` 使ってます。
自分の `init.el` から切り貼りしてるので、つじつま合わないところがあるかも。

```elisp
(use-package python
  :mode
  ("\\.py\\'" . python-mode)
  )

(use-package lsp-mode
  :config

  ;; .venv, .mypy_cache を watch 対象から外す
  (dolist (dir '(
                 "[/\\\\]\\.venv$"
                 "[/\\\\]\\.mypy_cache$"
                 "[/\\\\]__pycache__$"
                 ))
    (push dir lsp-file-watch-ignored))

  ;; lsp-mode の設定はここを参照してください。
  ;; https://emacs-lsp.github.io/lsp-mode/page/settings/

  (setq-default lsp-auto-configure t)
  (setq-default lsp-enable-completion-at-point t)

  ;; imenu-listを使う場合は(lsp-uiの imenu 統合を使わない場合は) nil にする
  ;(setq-default lsp-enable-imenu nil)

  ;; クロスリファレンスとの統合を有効化する
  ;; xref-find-definitions
  ;; xref-find-references
  (setq-default lsp-enable-xref t)

  ;; linter framework として flycheck を使う
  (setq-default lsp-diagnostics-provider :flycheck)

  ;; ミニバッファでの関数情報表示
  (setq-default lsp-eldoc-enable-hover t)

  ;; nii: ミニバッファでの関数情報をシグニチャだけにする
  ;; t: ミニバッファでの関数情報で、doc-string 本体を表示する
  (setq-default lsp-eldoc-render-all nil)

  ;; breadcrumb
  ;; 最上部にパンくずリストを表示する。
  ;; https://emacs-lsp.github.io/lsp-mode/page/settings/headerline/#lsp-headerline-breadcrumb-segments
  ;; lsp-headerline-breadcrumb-segments に指定できるキーワードは以下の通り。
  ;;   project
  ;;   file
  ;;   path-up-to-project
  ;;   symbols
  (setq-default lsp-headerline-breadcrumb-enable t)
  (setq-default lsp-headerline-breadcrumb-segments '(project file symbols))

  ;; snippet
  (setq-default lsp-enable-snippet t)

  ;; フック関数の定義
  ;; python-mode 用、lsp-mode コンフィグ
  (defun lsp/python-mode-hook
    ()
    (when (fboundp 'company-mode)
      ;; company をコンフィグする
      (setq
       ;; 1文字で completion 発動させる
       company-minimum-prefix-length 1
       ;; default is 0.2
       company-idle-delay 0.0
       )
      )
    )

  :commands (lsp lsp-deferred)
  :hook
  (python-mode . lsp) ; python-mode で lsp-mode を有効化する
  ; lsp-deferred だと、対象のバッファが visible になるまで lsp mode の起動を遅延させます、とのことだが違いを感じない。
  ;(python-mode . lsp-deferrred)
  (python-mode . lsp/python-mode-hook) ; python-mode 用のフック関数を仕掛ける
  )

(use-package lsp-pyright
  :init

  (defun lsp-pyright/python-mode-hook
    ()
    ;; lsp-pyright を有効化する
    (require 'lsp-pyright)
    (when (fboundp 'flycheck-mode)
        ;; pyright で lint するので、python-mypy は使わない。
        (setq flycheck-disabled-checkers '(python-mypy))
        )
    )

  :hook (python-mode . lsp-pyright/python-mode-hook)
  )

(use-package lsp-ui
  :after lsp-mode
  :config

  ;; ui-peek を有効化する
  (setq lsp-ui-peek-enable t)

  ;; 候補が一つでも、常にpeek表示する。
  (setq lsp-ui-peek-always-show t)

  ;; sideline で flycheck 等の情報を表示する
  (setq lsp-ui-sideline-show-diagnostics t)
  ;; sideline で コードアクションを表示する
  (setq lsp-ui-sideline-show-code-actions t)
  ;; ホバーで表示されるものを、ホバーの変わりにsidelineで表示する
  ;;(setq lsp-ui-sideline-show-hover t)

  :bind
  (:map lsp-ui-mode-map
        ;; デフォルトの xref-find-definitions だと、ジャンプはできるが、ui-peek が使えない。
        ("M-." . lsp-ui-peek-find-definitions)

        ;; デフォルトの xref-find-references だと、ジャンプはできるが、ui-peek が使えない。
        ("M-?" . lsp-ui-peek-find-references)
        )
  :hook
  (lsp-mode . lsp-ui-mode)
)

(use-package company
  :init
  (global-company-mode t)
  )

(use-package flycheck
  :init
  (global-flycheck-mode)
  )

```

## トラブルシュート

`M-x``lsp-doctor`で、lspの動作状態を表示してくれます。

```text
Checking for Native JSON support: OK
Check emacs supports `read-process-output-max': OK
Check `read-process-output-max' default has been changed from 4k: OK
Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available): OK
`gc-cons-threshold' increased?: OK
Using gccemacs with emacs lisp native compilation (https://akrl.sdf.org/gccemacs.html): OK
```

コード書いてて、lspの動きがおかしいな、ファイル解析してくれないなと思ったら、`M-x``lsp-workspace-restart`でlanguage server を再起動してくれるみたいです。
それでもだめなら、emacsを再起動。