CompanyからCorfuに移行する

# 背景
【2023年1月7日】大幅に更新しました。

こんばんは。Emacs歴２年の初心者です。
これまで入力補完には[company](https://company-mode.github.io/)を使用していましたが、[corfu](https://github.com/minad/corfu)＋[cape](https://github.com/minad/cape)が想像以上に良かったのと、設定に関する日本語記事があまりなかったので書いてみました。
[corfu](https://github.com/minad/corfu)に興味のある方の参考になれば幸いです。

# 前提条件
[corfu](https://github.com/minad/corfu)はEmacs27以上が必須になります。
また、~~CUI版Emacsでは使用できません。~~ [corfu-terminal](https://codeberg.org/akib/emacs-corfu-terminal)を利用するとCUI版でも使用できます。

# セットアップ
今回設定するパッケージは下記のとおりです。
- [corfu](https://github.com/minad/corfu)
- [cape](https://github.com/minad/cape)
- [orderless](https://github.com/oantolin/orderless)
- [prescient](https://github.com/radian-software/prescient.el)
- [kind-icon](https://github.com/jdtsmith/kind-icon)
- ~~[corfu-doc](https://github.com/galeo/corfu-doc)~~ `corfu` 本体に統合されました。（`corfu-popupinfo`）
- [yasnippet](https://github.com/joaotavora/yasnippet)
- [tempel](https://github.com/minad/tempel)
- [lsp-mode](https://github.com/emacs-lsp/lsp-mode)


## [corfu](https://github.com/minad/corfu)
まずは基本となる[corfu](https://github.com/minad/corfu)を設定します。
VSCodeのようにTABで補完し、RETはそのまま改行するように設定しています。
https://github.com/minad/corfu#tab-only-completion

```init.el
(use-package corfu
  :custom ((corfu-auto t)
           (corfu-auto-delay 0)
           (corfu-auto-prefix 1)
           (corfu-cycle t)
           (corfu-on-exact-match nil)
           (tab-always-indent 'complete))
  :bind (nil
         :map corfu-map
         ("TAB" . corfu-insert)
         ("<tab>" . corfu-insert)
         ("RET" . nil)
         ("<return>" . nil))
  :init
  (global-corfu-mode +1)

  :config
  ;; java-mode などの一部のモードではタブに `c-indent-line-or-region` が割り当てられているので、
  ;; 補完が出るように `indent-for-tab-command` に置き換える
  (defun my/corfu-remap-tab-command ()
    (global-set-key [remap c-indent-line-or-region] #'indent-for-tab-command))
  (add-hook 'java-mode-hook #'my/corfu-remap-tab-command)

  ;; ミニバッファー上でverticoによる補完が行われない場合、corfuの補完が出るようにします。
  ;; https://github.com/minad/corfu#completing-in-the-minibuffer
  (defun corfu-enable-always-in-minibuffer ()
    "Enable Corfu in the minibuffer if Vertico/Mct are not active."
    (unless (or (bound-and-true-p mct--active)
                (bound-and-true-p vertico--input))
      ;; (setq-local corfu-auto nil) ;; Enable/disable auto completion
      (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup
                  corfu-popupinfo-delay nil)
      (corfu-mode 1)))
  (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1)

  ;; lsp-modeでcorfuが起動するように設定する
  (with-eval-after-load 'lsp-mode
    (setq lsp-completion-provider :none)))
```

![corfu.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696232/fc646c62-2761-7e2a-eec4-7b788b5c1856.gif)

## [cape](https://github.com/minad/cape)

次に[cape](https://github.com/minad/cape)を設定します。
[corfu](https://github.com/minad/corfu)だけでも使用できますが、`completion-at-point`を自分好みにカスタマイズしたり、様々な`completion-at-point`を追加することができるようになります。今回は`tempel`や`tabnine`等も組み合わせています。

```init.el
(use-package tabnine
  :hook ((prog-mode . tabnine-mode)
         (text-mode . tabnine-mode)
         (kill-emacs . tabnine-kill-process))
  :bind (:map  tabnine-completion-map
	     ("TAB" . nil)
         ("<tab>" . nil))
  :init
  (tabnine-start-process)
  (global-tabnine-mode +1))

(use-package cape
  :hook (((prog-mode
           text-mode
           conf-mode
           eglot-managed-mode
           lsp-completion-mode) . my/set-super-capf))
  :config
  (setq cape-dabbrev-check-other-buffers nil)

  (defun my/set-super-capf (&optional arg)
    (setq-local completion-at-point-functions
                (list (cape-capf-noninterruptible
                       (cape-capf-buster
                        (cape-capf-properties
                         (cape-capf-super
                          (if arg
                              arg
                            (car completion-at-point-functions))
                          #'tempel-complete
                          #'tabnine-completion-at-point
                          #'cape-dabbrev
                          #'cape-file)
                         :sort t
                         :exclusive 'no))))))

  (add-to-list 'completion-at-point-functions #'tempel-complete)
  (add-to-list 'completion-at-point-functions #'tabnine-completion-at-point)
  (add-to-list 'completion-at-point-functions #'cape-file t)
  (add-to-list 'completion-at-point-functions #'cape-tex t)
  (add-to-list 'completion-at-point-functions #'cape-dabbrev t)
  (add-to-list 'completion-at-point-functions #'cape-keyword t))
```

![cape.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696232/e770646b-c897-6368-72fd-1bdb0e7ed99d.gif)

- `completion-at-point-functions`は複数のcompletion-at-pointが格納でき、格納された順番に評価されます。
- `cape-super-capf`は複数のcompletion-at-pointを一つにまとめることができます。
- `cape-wrap-buster`で常に最新の候補を表示するようにします。（https://github.com/minad/corfu/wiki#continuously-update-the-candidates）

## [orderless](https://github.com/oantolin/orderless)
次に[orderless](https://github.com/oantolin/orderless)を設定します。

Emacs標準の補完スタイルの`flex`でも構いませんが、`orderless`のほうが絞り込み速度が早く、後述の`prescient`と組み合わせることで使い勝手も向上します。

より高度な設定は[こちら](https://qiita.com/nobuyuki86/items/f12bbd126c4c3adbd7ec#%E5%BF%9C%E7%94%A8orderless%E3%81%A8prescient%E3%82%92%E7%B5%84%E3%81%BF%E5%90%88%E3%82%8F%E3%81%9B%E3%82%8B%E5%A0%B4%E5%90%88)で紹介しています。

```init.el
  (use-package orderless
    :init
    (setq completion-styles '(orderless basic)
          completion-category-defaults nil
          completion-category-overrides nil)

    :config
    ;; migemoでローマ字検索を有効にする
    (with-eval-after-load 'migemo
      (defun orderless-migemo (component)
        (let ((pattern (downcase (migemo-get-pattern component))))
          (condition-case nil
              (progn (string-match-p pattern "") pattern)
            (invalid-regexp nil))))
      (add-to-list 'orderless-matching-styles 'orderless-migemo))

    ;; corfuはorderless-flexで絞り込む
    (with-eval-after-load 'corfu
      (add-hook 'corfu-mode-hook
                (lambda ()
                  (setq-local orderless-matching-styles '(orderless-flex))))))
```

![orderless.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696232/fcf7ea25-d3ae-dd50-a2e7-5a75cad3328a.gif)

## [prescient](https://github.com/radian-software/prescient.el)
`presicent`は候補を履歴・頻度・長さの順に並び替えてくれるパッケージで、従来は`selectrum`や`ivy`、`company`のパッケージしか対応していませんでしたが、最近`vertico`や`corfu`も対応しました。

`prescient`を`completion-styles`にそのまま設定することもできますが、`orderless`の方がパフォーマンスが良いので、組み合わせています。

【余談】`hotfuzz`の作者が`completion-styles`のパフォーマンス測定用の[リポジトリ](https://github.com/axelf4/emacs-completion-bench)を公開してくれているので、興味がある方はお試しください。

```init.el
  (use-package prescient
    :config
    (setq prescient-aggressive-file-save t)
    (prescient-persist-mode +1))

  (use-package corfu-prescient
    :after corfu
    :config
    (with-eval-after-load 'orderless
      (setq corfu-prescient-enable-filtering nil))
    (corfu-prescient-mode +1))
```

![qiita.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696232/c12845c3-04a1-2fee-398a-d7bf3b5b8ff9.gif)

## [kind-icon](https://github.com/jdtsmith/kind-icon)
[corfu](https://github.com/minad/corfu)にアイコンをつけるパッケージです。

```init.el
(use-package kind-icon
  :after corfu
  :custom (kind-icon-default-face 'corfu-default) ; to compute blended backgrounds correctly
  :config
  (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))
```

![kind-icon.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696232/8d28d354-ef03-0f3a-f030-6cc5cd3840d3.gif)

## ~~[corfu-doc](https://github.com/galeo/corfu-doc)~~ `corfu-popupinfo`
補完候補の隣に説明が表示されるようになります。
`corfu`本体に統合され、`extensions`ディレクトリに格納されています。
`straight`や`elpaca`を使用している場合は`corfu`の宣言箇所に`:files (:defaults "extensions/*")`を追加します。

```init.el
(use-package corfu
  :straight (corfu :type git
                   :host github
                   :repo "minad/corfu"
                   :branch "async"
                   :files (:defaults "extensions/*"))
...（略）...
)

(use-package corfu-popupinfo
  :straight nil
  :after corfu
  :hook (corfu-mode . corfu-popupinfo-mode))
```

![corfu-doc.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696232/10920a74-58c8-8f3a-bd62-0c1595ed95a0.gif)

## [yasnippet](https://github.com/joaotavora/yasnippet)
`yasnippet`のキーは`corfu`よりも優先順位が高いため、`corfu`用に`TAB`へキーを割り当てても動作しません。
そのため、`yasnippet`の`TAB`キーを無効化して、別のキーに割り当ててます。

```init.el
  (use-package yasnippet
    :bind (nil
           :map yas-keymap
           ("<tab>" . nil)
           ("TAB" . nil)
           ("<backtab>" . nil)
           ("S-TAB" . nil)
           ("M-}" . yas-next-field-or-maybe-expand)
           ("M-{" . yas-prev-field))
    :init
    (yas-global-mode +1))
```

## [tempel](https://github.com/minad/tempel)

corfuの作者のminadさんがスニペットのパッケージを作成してくれています。
corfuとの統合はcapeに載せています。

```init.el
  (use-package tempel
    :bind (("M-+" . tempel-complete) ;; Alternative tempel-expand
           ("M-*" . tempel-insert)))
```


# 終わりに
[corfu](https://github.com/minad/corfu)は動作が軽量でカスタマイズ性が高く、設定を工夫すれば`company`と同等かそれ以上の使い勝手になります。
corfu wikiやREADMEに設定例を色々記載してくれているので、自分にあった最適な設定を見つけていきたいです。

# 参考
[Corfu Wiki](https://github.com/minad/corfu/wiki)