一番使い勝手の良いemacsのcompanyモードの設定を紹介

2023年4月03日
thumbnail
companyはauto-completeの次に出てきたEmacsのデファクトスタンダードとなった入力補完パッケージです。これを入れないでコーディングするのは考えられないほど強力なパッケージです。companyは基本設定だけでもかなり便利に使えますが、ここで紹介しているパッケージと設定を加えれば、より便利になるので、ぜひ試してみてください。
こちらの記事を大変参考にさせていただきました。
最初に完成したcompanyの設定コードを記述しておきます。
(use-package company
  :ensure t
  :after company-statistics
  :bind (("M-<tab>" . company-complete) ;; Tabで自動補完を起動する
         :map company-active-map
         ;; C-n, C-pで補完候補を次/前の候補を選択
         ("M-n" . nil)                      ;; M-nで次の候補への移動をキャンセル
         ("M-p" . nil)                      ;; M-pでの前の候補への移動をキャンセル
         ("C-n" . company-select-next)      ;; 次の補完候補を選択
         ("C-p" . company-select-previous);; 前の補完候補を選択
         ("C-s" . company-filter-candidates) ;; C-sで絞り込む
         :map company-search-map
         ;; 検索候補の移動をC-nとC-pで移動する
         ("C-n" . company-select-next)
         ("C-p" . company-select-previous))
  :init
  ;; 全バッファで有効にする
  (global-company-mode)
  :config
  (define-key emacs-lisp-mode-map (kbd "C-M-i") nil) ;; CUI版のためにemacs-lisp-modeでバインドされるC-M-iをアンバインド
  (global-set-key (kbd "C-M-i") 'company-complete)   ;; CUI版ではM-<tab>はC-M-iに変換されるのでそれを利用
  (setq completion-ignore-case t)
  (setq company-idle-delay 0)                    ;; 待ち時間を0秒にする
  (setq company-minimum-prefix-length 2)         ;; 補完できそうな文字が2文字以上入力されたら候補を表示
  (setq company-selection-wrap-around t)         ;; 候補の一番下でさらに下に行こうとすると一番上に戻る
  (setq company-transformers '(company-sort-by-occurrence company-sort-by-backend-importance))) ;; 利用頻度が高いものを候補の上に表示する

(use-package company-statistics
  :ensure t
  :init
  (company-statistics-mode))

;; auto-completeに近い挙動で候補の絞り込みができる
(use-package company-dwim
  :straight '(company-dwim
              :type git
              :host github
              :repo "zk-phi/company-dwim")
  :ensure t
  :init
  (define-key company-active-map (kbd "TAB") 'company-dwim)
  (setq company-frontends
      '(company-pseudo-tooltip-unless-just-one-frontend
        company-dwim-frontend
        company-echo-metadata-frontend)))

;; カーソルの位置がどこであってもcompanyを起動できる
(use-package company-anywhere
  :straight '(company-anywhere
              :type git
              :host github
              :repo "zk-phi/company-anywhere")
  :ensure t)

;; プログラムの関数、変数のキーワード補完を強化
(use-package company-same-mode-buffers
  :straight '(company-same-mode-buffers
              :type git
              :host github
              :repo "zk-phi/company-same-mode-buffers")
  :after company
  :ensure t
  :init
  (require 'company-same-mode-buffers)
  (company-same-mode-buffers-initialize)
  ;;
  :config
  (setq company-backends
        '((company-capf :with company-same-mode-buffers)
          (company-dabbrev-code :with company-same-mode-buffers)
          company-keywords
          company-files
          company-dabbrev)))

companyのインストールと基本設定

companyの基本バインドから説明します。companyのデフォルトバインドでは補完候補が表示さた時の移動がM-n or M-pで指がきついので、その設定を無効にして、代わりにC-n or C-pで移動できるようにしています。
:configにはより使いやすくするための変更が記述されています。 company-idle-delayは補完候補が表示されるまでの遅延です。デフォルトは0.2秒で遅く感じたので0秒にしています。
company-minimum-prefix-lengthは候補が表示されるまでの最低入力文字数です。デフォルトでは3文字以上で、これもやや遅く感じたので、2文字から候補が表示されるようにしています。
company-selection-wrap-aroundは候補を移動して、一番下、または一番上まで来たら候補をループできるようにしています。

表示順を使用頻度で並び替える

companyのcompany-transformersは表示順を管理しています。デフォルトではcompany-sort-by-occurrenceが設定されており、基本は50音順で表示され、使用頻度が高いものは優先して表示されます。
company-statisticsはサードパーティ製の表示順をスマートにするパッケージです。で、ここがトリッキーなところです。順当にcompany-statisticsを使う場合は、インストールして、company-transformersの並び順を(company-sort-by-statistics company-sort-by-backend-importance)に変更します。
ですがcompany-sort-by-statisticsを指定すると、思ったような順番に候補を表示してくれなかったので、あえてデフォルトのままcompany-transformersを使っています。じゃあcompany-statisticsいらないじゃんと思いきや、これがないとそれはそれで候補の順番が異なってしまいます。
実際に使ってみましょう。例えば、このような3つの関数があったとします。
(defun show-file-name ()
  "Show the full path file name in the minibuffer."
  (interactive)
  (message (buffer-file-name)))
(defun show-file-name2 ()
  "Show the full path file name in the minibuffer."
  (interactive)
  (message (buffer-file-name)))
(defun show-file-name3 ()
  "Show the full path file name in the minibuffer."
  (interactive)
  (message (buffer-file-name)))
直近で記述した関数がshow-file-name3なら候補の一番上に表示されます。
article image

フロントの表示に関する設定

company-frontends関数はcompanyのUI挙動を担います。 デフォルトでは(company-pseudo-tooltip-unless-just-one-frontend company-preview-if-just-one-frontend company-echo-metadata-frontend)が設定されています。これらの挙動をから解説します。
company-pseudo-tooltip-unless-just-one-frontenは入力候補がある場合、ツールチップを表示する宣言です。これが宣言されていないと、ツールチップが表示されません。
article image
company-preview-if-just-one-frontendはもしも、入力候補が1つしかない場合でもその候補をエディタ上で表示するという宣言です。
例えば、入力補完を使うわずに(file-attribute-まで入力してaとタイプすると、それにマッチする唯一の候補がエディタ上に表示されます。
article image
company-echo-metadata-frontendは候補で選択している関数や変数にメタデータがあれば、それをエコーエリアに表示します。
article image

候補の選択を変更する

company-frontendsを編集することで、タブによる候補絞り込みの挙動を変更します。 companyの候補選択はやたらと、文字入力による絞り込みを推してきます。 一方で昔よく使われていたauto-completeはタブでうまい具合に目的の単語を絞り込むことができていました。company-dwimはその文化を踏襲しています。
このパッケージを作ったzk-phiの記事にもある通り、companyにもタブ絞り込みのcompany-tng-modeがあります。
company-tng-modeは挙動がかなり微妙なので、不要かと思いますが、そちら説明をした上でcompany-dwimがどれほど素晴らしいか説明します。

company-tng-mode

公式リポジトリはこちらになります。
company-tng-modeはcompanyを入れた時点で入っています。試しに使うためにM-x company-tng-modeで有効にします。
例えば、あやふやな記憶の中でcoding-system-lesspcoding-system-listの2つがあり、その内のcoding-system-lesspを入力したいとします。
(codとまで打ち込み、候補を見てみます。
article image
50音順ではlは後ろの方なので、最初の候補一覧には表示されていません。
ここでタブを押すと、候補の一番上coding-system-aliasesに選択が当たります。company-tng-modeは一度でもタブを押したら、あとは候補をタブで連打して移動する方法でしか絞り込めません。
理想的には共通するcoding-system-までが補完されて、あとはlをタイプするだけで残りの2つに絞り込める方が良いですよね。
もう1つ微妙なのは、候補を選択するためにエンターを押すと、改行されてしまう点です。
article image
article image
以上がcompany-tng-modeの入力補完機能です。結局使わないので、解説は不要かと思いましたが、何がダメなのかを知っておくのは良いと思うので載せておきました。

company-dwim

同じようにcoding-system-lessp を入力したいとしましょう。(codとまで打ち込み、候補を見てみます。
article image
共通した候補の(coding-system-まで、タブを一回押すことで補完してもらいます。
article image
lをタイプすると、候補が2つに絞られます。そのままエンターを押せば入力補完されます。
article image
改行もされません。
article image

company-anywhereでどこでも補完する

company-anywhereは同じくzk-phiさんが作ったcompanyの補完機能を強化するパッケージです。companyは補完が始まる条件として、カーソルの直後にスペースがないと発動しません。それをどこであってもM-<tab>で補完を発動させます。設定は特に必要ないです。

同一シンボルを補完

companyの補完候補を探すアルゴリズムはcompany-backendsで指定されています。列挙された上からマッチするか試され、マッチした場合は候補リストが表示されます。なので定義する項目と順番が重要になります。

デフォルトの設定

まずはデフォルトのbackendsを見てみましょう。
(company-bbdb
 company-semantic
 company-cmake
 company-capf
 company-clang
 company-files
 (company-dabbrev-code company-gtags company-etags company-keywords)
 company-oddmuse
 company-dabbrev)
よく使われるものから説明していきます。
company-capfは公式サイトによれば、lisp、css、nxml系の入力をサポートするものです。elispで設定を作っているときは欠かせません。逆にlisp以外のプログラム言語の補完はやってくれないので、ライトユーザーはそこまで恩恵がありません。
company-filesは文字列でディレクトリのパスが記述されると、そのファイルから見たファイル指定への補完が走ります。
(company-dabbrev-code company-gtags company-etags company-keywords)はリストでまとめられています。このように書くと、リスト内のbackendエンジン全てで検証されてマッチするものがあるかどうか判断され、どれかにマッチした場合は結果が返されます。
company-dabbrev-codeはプログラミング系で起動してdabbrevと近い挙動で補完してくれます。company-dabbrevはキャメルケースやスネークケースを考慮しませんが、こちらはその辺りを考慮して補完してくれます。
company-keywordsは各プログラム言語で使う予約語等を補完してくれます。
company-dabbrevはdabbrevのキーワードにマッチするものを補完してくれます。
company-gtagsはサードパーティ製のgtags-modeで補完されるエンジンです。それをあえて使っていない限りは使いません。company-etagsgtagsと同じ感じなのかなぁ〜?検索してもよくわかりません。
company-semanticcompany-cmakecompany-clangはそれぞれC言語系の入力補完をサポートしてくれます。
company-bbdbbbdbという電子メールのアドレス、電話番号などのアドレス帳を提供する機能を補完します。
company-oddmuseoddmuseというemacs関連のwikiを編集するメジャーモードで、wikiの書き方を補完してくれるっぽいです。

キーワード補完を強化

company-same-mode-buffersは同一のメジャーモードで開いているバッファ内からキーワードを探索して補完してくれます。詳しくは作者のこの部分を読むとどういったことができるかわかります。
compan-keywordsと似ていますが、より強化されているので、こちらを優先的に使えるように設定しています。同時にあまり使うことがなさそうなbackendsを取り除いています。
  (setq company-backends
        '((company-capf :with company-same-mode-buffers)
          (company-dabbrev-code :with company-same-mode-buffers)
          company-keywords
          company-files
          company-dabbrev)))

まとめ

companyの細かい記述と使いやすい設定を紹介しました。 もしも、今後より使いやすい何かが見つかった場合はそれも加えて紹介したいと思います。
profile image
Ted
大学でコンセンサスアルゴリズムを研究。卒業後ベンチャー企業に入社してフルスタックでWebサービスを開発。現在は大手IT企業に転職し、プログラミングを行っている。AIにプログラマーの仕事を奪って欲しいと願っている。