LoginSignup
30
15

CompanyからCorfuに移行する

Last updated at Posted at 2022-02-07

背景

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

こんばんは。Emacs歴2年の初心者です。
これまで入力補完にはcompanyを使用していましたが、corfucapeが想像以上に良かったのと、設定に関する日本語記事があまりなかったので書いてみました。
corfuに興味のある方の参考になれば幸いです。

前提条件

corfuはEmacs27以上が必須になります。
また、CUI版Emacsでは使用できません。 corfu-terminalを利用するとCUI版でも使用できます。

セットアップ

今回設定するパッケージは下記のとおりです。

corfu

まずは基本となる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

cape

次にcapeを設定します。
corfuだけでも使用できますが、completion-at-pointを自分好みにカスタマイズしたり、様々なcompletion-at-pointを追加することができるようになります。今回はtempeltabnine等も組み合わせています。

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

  • 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

次にorderlessを設定します。

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

より高度な設定はこちらで紹介しています。

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

prescient

presicentは候補を履歴・頻度・長さの順に並び替えてくれるパッケージで、従来はselectrumivycompanyのパッケージしか対応していませんでしたが、最近verticocorfuも対応しました。

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

【余談】hotfuzzの作者がcompletion-stylesのパフォーマンス測定用のリポジトリを公開してくれているので、興味がある方はお試しください。

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

kind-icon

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

corfu-doc corfu-popupinfo

補完候補の隣に説明が表示されるようになります。
corfu本体に統合され、extensionsディレクトリに格納されています。
straightelpacaを使用している場合は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

yasnippet

yasnippetのキーはcorfuよりも優先順位が高いため、corfu用にTABへキーを割り当てても動作しません。
そのため、yasnippetTABキーを無効化して、別のキーに割り当ててます。

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

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

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

終わりに

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

参考

Corfu Wiki

30
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
15