Emacsで新しいFuzzy補完 - fussy -

# 背景

　こんばんは。init.elをいじり続けてる初心者です。
最近[orderless](https://github.com/oantolin/orderless)のように`completion-styles`を高度にカスタマイズして、絞り込みを強化する流れが来ています。（[Emacsの次世代ミニバッファ補完UI](https://blog.tomoya.dev/posts/a-new-wave-has-arrived-at-emacs/)）
　しかし、[company](https://company-mode.github.io/)等の入力補完でFuzzyに絞りたいとき、Emacs標準の`flex`や`orderless-flex`を使ってもパフォーマンスが悪かったり、[company-fuzzy](https://github.com/jcs-elpa/company-fuzzy)を使用してもうまく動かないことがあったり、なかなかFuzzyな入力に満足いきません。
　そんな中、これらの問題を解消してくれそうなパッケージを見つけたので紹介します。

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

　`fussy` は `completion-styles` に組み込むことで、Fuzzyに絞り込みをしてくれるパッケージです。 `flex` と大きく異なる点はスコアリングとフィルタリングのバックエンドを変更できる点です。

**スコアリング**

補完候補の並び替えに影響します。

- `flx`（デフォルト） - キャッシュを多く使用し、`flex`よりも高速
- `flx-rs` - `flx`をRust言語によって実装して、パフォーマンスを更に向上
- `fzf-native` - [telescope-fzf-native.nvim](https://github.com/nvim-telescope/telescope-fzf-native.nvim)の動的モジュールを使用して、fzfのスコアリングを適用します
- `fuz` - `skim`と`clangd`のアルゴリズムを持ち、Rust言語で実装
- `liquidmetal` - Quicksilverのスコアリングアルゴリズムを模倣して作成されています
- `sublime-fuzzy` - Sublimeの絞り込みをRust言語で実装
- `hotfuzz` - `flex`よりもスコアリングが優秀（[hotfuzz](https://github.com/axelf4/hotfuzz))

**フィルタリング**

補完候補の絞り込みに影響します。

- `flex`（デフォルト）
- ~~`fast`~~ `default` - C言語で記述されている`all-completions`を使用します。
- `orderless` `orderless-flex` - `orderless`のフィルタリングを使用します。

# 設定

- `fussy-filter-default`では`fussy-default-regex-fn`と`fussy-prefer-prefix`を変更することで絞り込み方法が変わります。
- `fussy-fuz-use-skim-p`を`t`にすると、`skim`アルゴリズムが採用され、`nil`にすると`clangd`が採用されます。

設定は下記のようになります。

```init.el

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; #fussy

(straight-use-package '(fussy :type git :host github :repo "jojojames/fussy"))
;; (straight-use-package '(flx-rs :repo "jcs-elpa/flx-rs" :fetcher github :files (:defaults "bin")))
(straight-use-package '(fzf-native :repo "dangduc/fzf-native" :host github :files (:defaults "bin")))
;; (straight-use-package '(fuz-bin :repo "jcs-elpa/fuz-bin" :fetcher github :files (:defaults "bin")))
;; (straight-use-package 'liquidmetal)
;; (straight-use-package '(sublime-fuzzy :repo "jcs-elpa/sublime-fuzzy" :fetcher github :files (:defaults "bin")))
;; (straight-use-package 'hotfuzz)
;; (straight-use-package 'orderless)

(require 'fussy)

(setq completion-styles '(fussy)
      completion-category-defaults nil
      compleiton-category-overrides nil
      ;; fussy-score-fn #'flx-score
      ;; fussy-score-fn 'flx-rs-score
      fussy-score-fn 'fussy-fzf-native-score
      ;; fussy-score-fn 'fussy-fuz-bin-score
      ;; fussy-score-fn 'fussy-liquidmetal-score
      ;; fussy-score-fn 'fussy-sublime-fuzzy-score
      ;; fussy-score-fn 'fussy-hotfuzz-score
      ;; fussy-filter-fn 'fussy-filter-flex
      fussy-filter-fn 'fussy-filter-default
      ;; fussy-filter-fn 'fussy-filter-orderless-flex
      ;; fussy-filter-fn 'fussy-filter-orderless
      )

;; (flx-rs-load-dyn)
(fzf-native-load-dyn)
;; (fuz-bin-load-dyn)
;; (sublime-fuzzy-load-dyn)

(with-eval-after-load 'company
  (defun j-company-capf (f &rest args)
    "Manage `completion-styles'."
    (let ((fussy-max-candidate-limit 5000)
	  (fussy-default-regex-fn 'fussy-pattern-first-letter)
	  (fussy-prefer-prefix nil))
      (apply f args)))

  (advice-add 'company-capf :around 'j-company-capf))
```

# 比較

スペック
- CPU - Inter(R) Celeron(R) CPU 1005M @ 1.90GHz 
- メモリ - ４GB 

companyの構成
- `company-prescient`
- `company-box`
- `company-capf` + `company-yasnippet` + `company-dabbrev`

## flex

![flex](https://user-images.githubusercontent.com/76552003/171414107-07e81965-ba37-4ffa-9788-c8886623f717.gif)

## fussy (liquidmetal + ~~fast~~ default)

![fussy](https://user-images.githubusercontent.com/76552003/171198437-1ad53968-1aee-4133-a49b-d83486844f67.gif)

低スペックなPCでもサクサク動きます。

# 終わりに

　[fussy](https://github.com/jojojames/fussy)は最近できたばかりのパッケージ（2022年5月31日時点）のようで、今後大きな変化があるかもしれません。
　しかし、とてもサクサク動いていいパッケージなので今後も使い続けたいと思います。