Emacsにおける入力補完と補完スタイル

メリークリスマス！[Emacs Advent Calendar](https://qiita.com/advent-calendar/2024/emacs)です。
本稿では、Emacsの入力補完とその種類を紹介します。

コード補完や補完インタフェースのパッケージを導入していると、利用しない・知らない方も多いかもしれません。
ただ最近はcorfuやverticoといったパッケージが話題になり、Emacs標準の入力補完への回帰の気運が高まっています。

そこで、ぜひこの機会にEmacsの入力補完について知り、さらにはぜひ使ってみてください。

## Emacsの入力補完とは

これです！
素のEmacsでTABを押したりしたときに出てきてｳﾜｰっとなるやつです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42721/3c7f7388-fa28-8ddd-0af5-b87854cb97b0.png)

この例では、入力補完を行う関数`completion-at-point`が呼び出され表示されています。
デフォルトキーバインドは`C-M-i`で、モードによっては`<TAB>`に割り当てられることもあります。

このEmacsの入力補完は、他にも

- 変数・関数・クラスなどのコード入力
- switch-to-buffer（`C-x b`）でのバッファの選択
- find-file（`C-x C-f`）でのファイルの選択
- execute-extended-command（`M-x`）でのコマンド名
- などなど…

と、多くの場面で利用されています。
おもしろいのは、一般的には区別されがちなコード入力補完とファイル選択補完などが、Emacsでは同じく扱われていることです。

とはいえ、読者の多くは、

- コード補完
  - auto-complete
  - company
  - corfu
- 補完インタフェース
  - ido
  - helm
  - anything
  - ivy
  - vertico

のようなパッケージを導入し、すでにこのスクリーンショットのような画面は見かけないかもしれません。

## Emacsの補完スタイルと設定

入力補完における重要な仕事は、大きく分けて「フィルター」と「ソート」の2つです。**（要出典）**

始めに入力補完を開始するとき、補完候補は多数が存在します。[^codecompletion]
入力補完システムはユーザ入力を受け取り、補完候補の絞り込みをします。（フィルター）
絞り込まれた新たな補完候補は、ユーザに有益な順に表示されます。（ソート）
なお、パッケージによってはソートではなく、スコアリングと呼ばれることもあります。

[^codecompletion]: コード補完における補完候補は、変数`completion-at-point-functions`に設定された関数リストで作られます。省略形である`capf`も変数・関数名としてよく登場しますね。

これら入力補完のアルゴリズムは、フィルター・ソートの速度のように明らかな優劣がつくものから、利用者の好みのように優劣が曖昧なものもあります。
そのため世の中には様々なアルゴリズムが存在しています。
Emacsではこの入力補完におけるアルゴリズムを補完スタイルと呼んでいます。

Emacsでは、補完スタイルを変数`completion-styles`で設定できます。
この変数は補完スタイルのリストになっています。
Emacsはリストの先頭の補完スタイルから試し、補完候補が0個だったら次の補完スタイルにフォールバックします。

また、ファイルの入力補完だけは別なアルゴリズムを使いたい、などと思うかもしれません。
そのためにEmacsは変数`completion-category-overrides`を用意しています。
これを使うと、入力補完を行うカテゴリごとに別な`completion-styles`を設定できます。
どんなカテゴリがあるか詳しくはドキュメントを参照してみてください。

このように入力補完ひとつ見てもユーザが自由で細やかに設定でき、自由なエディタであるEmacsならではの良いところだと思います。

## Emacs標準の補完スタイル

Emacs29[^emacsversion]では標準の補完スタイルとして8種類が用意されています。
ここではそれらの簡単な説明と、入力に対するマッチ対象を正規表現の記法で紹介します。

[^emacsversion]: 本稿時の最新メジャーバージョン。

Emacsの補完スタイルによってはカーソル位置を利用するものもあります。
そのために、例ではカーソル位置をバー記号`|`で表します。

またEmacs標準の補完スタイルでは、一部を除きソートはアルファベット順になるようです。

### emacs21

接頭辞ベースのシンプルな補完スタイルです。
入力補完開始時に文字列のどこにカーソルがいるかを考慮しません。
また入力補完をしようとすると、カーソルがいる文字列の最後にカーソルが移動します。

- 入力：`foobar`
- マッチ：`foobar.*`

### emacs22

カーソル位置に対する接頭辞ベースの補完スタイルです。
カーソルよりも前を入力として入力補完します。

- 入力：`foo|bar`
- マッチ：`foo.*`

### basic

カーソル位置に対する接頭辞・接尾辞ベースの補完スタイルです。
カーソルの前後両方を考慮してマッチするものを探します。

- 入力：`foo|bar`
- マッチ：`foo.*bar.*`

### partial-completion

単語の区切りを認識する接頭辞ベースの補完スタイルです。
区切りにはハイフン・アンダーバー・スラッシュなど複数が反応するようです。

- 入力：`foo-ba|r`
- マッチ：`foo.*-ba.*r.*`

この補完スタイルはファイルパスにとても便利です。
例えば入力`/u/l/s`が`/usr/local/share`にマッチします。

 ### substring

名前の通りサブストリングベースの補完スタイルです。
カーソル位置の前後両方がサブストリングマッチするか確認します。
今までとの違いは、fooよりも前にも何らかの文字列が許されるところです。

- 入力：`foo|bar`
- マッチ：`.*foo.*bar.*`
 
### flex

fuzzyな補完スタイルです。
入力文字列を1文字ずつに分解し、それらが順に含まれているすべての文字列にマッチします。
マッチが複雑なためか、ソートがアルファベット順になりません。

標準外にも多数のfuzzy補完スタイルのパッケージが提供されています。

- 入力：`foo`
- マッチ：`.*f.*o.*o.*`

### initials

イニシャルベースの補完スタイルです。
コマンド名やファイルパスをイニシャルによる省略でマッチさせようとします。

例えば、入力`capf`が`completion-at-point-functions`にマッチします。
ファイルシステムに対しては、入力`~/sew`が`~/src/emacs/work`にマッチします。

ドキュメントにない補完条件があるようで、マッチできるときとできないときがあります。
補完が一意に決まるときでしょうか？
思わぬ部分補完が行われることがあるため、単独で使うには使いづらい補完スタイルです。

- 入力：`fgh`
- マッチ：`f.*-g.*-h.*`
  - 正確にはハイフン以外の区切字にもマッチ

### shorthand

自分で登録した略語ベースでの補完スタイルです。
あらかじめ変数`read-symbol-shorthands`に略語を登録し、それを考慮した補完が可能です。

## パッケージで提供されている他の補完スタイル

Emacs標準の補完スタイル以外にも、多数の補完スタイルがパッケージとして開発・提供されています。
その中でも私が使っているものや、以前使っていたものを紹介しようと思います。

### [orderless](https://github.com/oantolin/orderless)

近年に登場し急激にシェアを伸ばしている新星の補完スタイルです。
スペース区切りによる柔軟で現代的なマッチができます。
スペースで区切ったサブクエリがどうマッチするか（substring、regexpなど）も細かに設定できます。
さらにディスパッチャという概念があり、特定の記号を入力することで一時的にマッチ方法を変更したりもできます。[^orderlessampersand]
またメンテナンス性をひじょうに重視しており、シンプルで簡潔な実装になっているそうです。

[^orderlessampersand]: `&`記号は`orderless-annotation`に割り当てられている。そのためorderlessを使いながらアンパサンド記号にマッチするには`=&`と入力するとよい。`=`は`orderless-literal`を設定する記号。誰かに有益かもしれない。

私は現在これをメインに愛用しています。
具体的にはorderlessの`orderless-literal`（完全マッチ）、`orderless-regexp`（正規表現マッチ）、`orderless-initialism`（イニシャルマッチ）を使っています。[^orderlessdefault]

[^orderlessdefault]: `oederless-prefixes`（接頭辞マッチ）と`orderless-flex`（fuzzyマッチ）がordelessのデフォルトではオンになっている

- 入力：`foo bar`
- マッチ：`(.*foo.*bar.*)|(.*bar.*foo.*)`
  - 正確には、サブクエリ`foo`のイニシャルマッチやfuzzyマッチも行われるため、より複雑

### [prescient](https://github.com/radian-software/prescient.el)

orderlessと二大巨頭と言っても過言ではない、有名な古株の補完スタイルです。
orderlessと同様にスペース区切りによる柔軟なマッチが可能です。
さらに使用頻度を考慮したソートが特徴です。
corfu、company、vertico、ivy、など他パッケージへの対応も豊富です。

私も以前はswiperと組み合わせて利用していました。
最近使ったコマンドや補完が選択肢の先頭にくるのが使いやすかったです。

現在はEmacs標準のsavehistを使い、履歴を保存することで同等の機能を実現しています。

### [hotfuzz](https://github.com/axelf4/hotfuzz)

Emacs標準である`flex`と同様のfuzzyな補完スタイルです。
ソートするためのスコアのつけ方を工夫してより使いやすくしています。
また動的モジュールのビルドに対応していて、とても高速です。

私はこの動的モジュールをビルドして、コード補完に愛用しています。
コード補完はfuzzyなマッチ派です。
速度は正義！


### [fussy](https://github.com/jojojames/fussy)

fuzzyな補完スタイルをより細かく設定したり組み合わせたりできるパッケージです。
flx、flx-rust、fzf-native、fuz、liquidmetal、sublime-fuzzy、hotfuzzと、多数のfuzzyな補完スタイルに対応しています。
若干のオーバーヘッドがあるようで、[こちらのベンチマーク](https://github.com/axelf4/emacs-completion-bench)が参考になりました。

### [migemo.el](https://github.com/emacs-jp/migemo)

Emacsのiseachでローマ字入力のまま日本語を扱えるようにするパッケージです。
アルファベットをひらがな・カタカナ・漢字単体・単語読みにマッチできます。
独自の日本語辞書を持つことで複雑な読み仮名マッチを実現しています。

例えば、入力`ai`に対して、`アイ`・`愛`・`相性`の先頭部、がマッチできます。
すごい！

これ自体は補完スタイルではないです。
しかしmigemo.elの関数をorderlessのマクロとともに使うことで、補完スタイルとしても使うことができます。

私は検索やkill-ringなどの一部カテゴリでorderlessと組み合わせて使っています。[参考](https://nyoho.jp/diary/?date=20210615)
IMを切り替えることなく日本語も一緒に検索できるのが便利です。

### [swiper](https://github.com/abo-abo/swiper)

これは補完スタイル単体ではなく、fuzzy finder的にコード検索を行うパッケージです。
内部では、fuzzy finder本体のivy、それを利用した便利関数集counsel、を含みます。

このパッケージでは、入力補完システムと補完スタイルを一体のパッケージとして提供しています。
以前はanythingやhelmのように一体化したパッケージが主流でした。
最近は入力補完システムと補完スタイルを分離して提供する`vertico`と`orderless`のようなパッケージも出てきています。

私も以前はswiper派でコントリビューションもしていました。
今はverticoとorderless（さらにconsult）を利用するようになりました。

## まとめ

- Emacsの補完スタイルは自由で細やかに設定ができる
- Emacs標準だけでなく外部パッケージとして多数の補完スタイルが提供されている
- Emacsの設定沼は楽しい

ぜひ皆さんも自分だけの最高の補完スタイルを設定してみてください。
その際に本稿が少しでも役立てば幸いです。

## 私のinit.el

おまけに私の補完スタイルのリンクを載せておきます。

- [GitHub - orderless項](https://github.com/yonta/dotfiles/blob/7c53daf87f78f721919bb3c3cc3a6fcd0a2b2b2d/.config/emacs/lisp/init_package.el#L208)
  - ベースの補完スタイルはorderless
  - 利用するのは`orderless-literal`、`orderless-regexp`、`orderless-initialism`
  - orderlessとmigemoを同時に使う`orderless+migemo`を定義
  - カテゴリによりmigemoを使う・使わないを切り替え
  - fileカテゴリに`partial-completion`を利用
- [GitHub - corfu項](https://github.com/yonta/dotfiles/blob/7c53daf87f78f721919bb3c3cc3a6fcd0a2b2b2d/.config/emacs/lisp/init_package.el#L371)
  - コード補完はcorfuで、補完スタイルはhotfuzz