helm を背に ivy の門を叩く



# はじめに

`ivy` は， `helm` と双璧を成す Emacs の補完システムです．リスト表示された多くの選択肢から，自分が使いたいものを高速に絞り込んで，効率良く選び出す．このシンプル，かつ極めて重要なタスクを， `ivy` や `helm` が手助けしてくれます．

過去を振り返れば，特に理由もなく私は `anything.el` から（強いて言えばメンテナス状況が良かった） `helm.el` に移行し，そして今回， `helm.el` から `ivy.el` に移行することにしました． `ivy` への移行の理由はいくつかありますが，シンプル軽量であり，ミニバッファで完結するインターフェイスに安定感があり，なにより `all-the-icons.el` との相性が良いことです．

特に普段からモードラインを表示しない派の方は，全体的にスッキリ・シャープな Emacs に生まれ変わりますので，移行をオススメします．

よければEmacs勉強会の[講演資料](https://speakerdeck.com/takaxp/counsel)と[設定集](https://gist.github.com/takaxp/86158fe74b8268843571c42b48a4336c)もご参照ください．また， `ivy` 以外の[設定集](https://takaxp.github.io/init.html)も公開しています．

![Untitled.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12232/49f6248e-470c-83d8-fc21-6e2773848a07.gif)
↑GUI

![Untitled.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12232/1a932caf-5f7f-5408-4e58-8386e7f1a4bb.gif)
↑ターミナル


# インストール

いくつかのインストール方法がありますが，パッケージ管理ツールを使うのが楽です． `ivy.el` をインストールする時は，プリセット関数を集めている `counsel.el` と， `helm-swoop` 相当の機能を提供する `swiper.el` も同時に入れるのが一般的です．

`counsel.el` が `swiper.el` に， `swiper.el` が `ivy.el` に依存しているので，パッケージ管理ツールを使う場合は， `counsel.el` をインストールすれば，3つのパッケージを同時にインストールできます．うまくいかない場合は，3つとも指定すれば良いです&#x2026;

なお当方の環境は `macOS` で，それ以外の環境では動作未確認ですが，大きな違いは無いと思います．


## MELPA

`counsel` をインストールします．Cask使いの場合は，一行追加で．

```el
(depends-on "counsel")
```


## el-get

`swiper` のリポジトリを指定します．

```el
(el-get-bundle "abo-abo/swiper")
```


# 基本設定

ここでは基本的な設定と配色， `ivy` の流儀と選択候補のソーティング変数を紹介します．


## 設定例

個別の設定や細かなハックに移る前に，まずは，これさえあればとりあえず動くという設定を施します．


### ivy.el

まずは `ivy` を使えるようにします．ほぼ素のままで良いです．

```el
(when (require 'ivy nil t)

  ;; M-o を ivy-dispatching-done-hydra に割り当てる．
  (require 'ivy-hydra)

  ;; `ivy-switch-buffer' (C-x b) のリストに recent files と bookmark を含める．
  (setq ivy-use-virtual-buffers t)

  ;; ミニバッファでコマンド発行を認める
  (when (setq enable-recursive-minibuffers t)
    (minibuffer-depth-indicate-mode 1)) ;; 何回層入ったかプロンプトに表示．

  ;; ESC連打でミニバッファを閉じる
  (define-key ivy-minibuffer-map (kbd "<escape>") 'minibuffer-keyboard-quit)

  ;; アクティベート
  (ivy-mode 1))
```


### counsel.el

`counsel.el` は， `ivy` の骨格関数である `ivy-read` を様々なコマンドに拡張し，それらをプリセットとして収録しています．全文探索の `counsel-ag` など，よく使うコマンドを別途インストールする必要がありません．

```el
(when (require 'counsel nil t)

  ;; キーバインドは一例です．好みに変えましょう．
  (global-set-key (kbd "M-x") 'counsel-M-x)
  (global-set-key (kbd "M-y") 'counsel-yank-pop)
  (global-set-key (kbd "C-M-z") 'counsel-fzf)
  (global-set-key (kbd "C-M-r") 'counsel-recentf)
  (global-set-key (kbd "C-x C-b") 'counsel-ibuffer)
  (global-set-key (kbd "C-M-f") 'counsel-ag)

  ;; アクティベート
  (counsel-mode 1))
```


### swiper

`swiper` は， `helm-swoop` のような文字列探索機能を提供します．カーソル位置の単語をキーに，バッファ内を探索．絞り込まれた選択候補を `C-n/C-p` で変えると，それに合わせてバッファの表示も変わり，連続してプレビューできます．なお， `swiper-all-thing-at-point` も実装されていて，これは開いているすべてのバッファを探索します．

![Untitled.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12232/945532c5-726a-7747-1e40-5dccf95052d7.gif)

```el
(when (require 'swiper nil t)

  ;; キーバインドは一例です．好みに変えましょう．
  (global-set-key (kbd "M-s M-s") 'swiper-thing-at-point))
```


## 検索語のハイライト

デフォルトの配色は，なかなかに個性的なので自分の好みに書き換えましょう．中途半端に書き換えると，デフォルトの配色が表に出てくるので，次にリストしたフェイスはすべて書き換える方が安全です．なお， `(setq ivy-display-style t)` とすると，配色を無効にできます．

![Screen Shot 2019-08-03 at 13.07.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12232/744505f0-f2c2-d7aa-a5cf-4aea008f6d2d.png)

-   ivy-current-match
    
    -   ミニバッファ内カーソル行の配色． `:background` を使うと，絞り込み中のカーソル行の文字色にも反映されてしまう． `:distant-foreground` を使うと反映されない．ただし，指定する色の明るさに依存して期待通りにならないこともある．微調整が必要．
-   ivy-minibuffer-match-face-1
    
    -   単語間を埋めるために使われる．ただし， `ivy-re-builders-alist` で指定するフィルタの種別に依存して使われたり，使われなかったりする．デフォルトの `ivy--regex-plus` の場合は，使われる．
        
        ![Screen Shot 2019-08-03 at 12.57.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12232/198cc1c6-f676-b703-c121-ec83cab34217.png)
-   ivy-minibuffer-match-face-{2,3,4}
    
    -   検索語のハイライトに使われる．検索語が3つを超える時は，ループして使う．
        
        ![Screen Shot 2019-08-03 at 13.02.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12232/64f74be6-f61c-07c6-7750-c409e327bd9e.png)

```el
(custom-set-faces
 '(ivy-current-match
   ((((class color) (background light))
     :background "#FFF3F3" :distant-foreground "#000000")
    (((class color) (background dark))
     :background "#404040" :distant-foreground "#abb2bf")))
 '(ivy-minibuffer-match-face-1
   ((((class color) (background light)) :foreground "#666666")
    (((class color) (background dark)) :foreground "#999999")))
 '(ivy-minibuffer-match-face-2
   ((((class color) (background light)) :foreground "#c03333" :underline t)
    (((class color) (background dark)) :foreground "#e04444" :underline t)))
 '(ivy-minibuffer-match-face-3
   ((((class color) (background light)) :foreground "#8585ff" :underline t)
    (((class color) (background dark)) :foreground "#7777ff" :underline t)))
 '(ivy-minibuffer-match-face-4
   ((((class color) (background light)) :foreground "#439943" :underline t)
    (((class color) (background dark)) :foreground "#33bb33" :underline t))))
```


## 最初に知っておくべき機能

`ivy` への移行に際して，知っておくべきデフォルト機能・コマンドがあります．それらは `helm` とは流儀が異なるので，少々戸惑うかもしれませんが，すぐ慣れます．

`counsel-M-x` 等で `ivy` インターフェイスで選択候補リストを表示した状況では，次のコマンドを使えるようになります．

| コマンド | 効果                                                           |
|:---------|:---------------------------------------------------------------|
| M-o      | 選択項目に対するアクションを選択できる                         |
| C-o      | ミニバッファを表示したままコマンドを発行．jk移動．gプレビュー．|
| C-M-n    | 候補を切り替えるとバッファも切り替わりプレビューできる（順方向）|
| C-M-p    | 候補を切り替えるとバッファも切り替わりプレビューできる（逆方向）|
| C-M-m    | 候補リストの表示を維持したまま，選択候補をプレビューする       |
| C-M-j    | 選択候補を無視して，入力中の値を使う                           |

`C-M-m`, `C-M-n`, `C-M-p` は，[helm-file-preview](https://github.com/elpa-host/helm-file-preview) の機能をデフォルト実装した印象です． `swiper` もそうですが， `QuickLook` 的にファイルの内容を確認できるのがとても便利です．

その他， `M-x ivy-resume` すれば，いつでも前回のセッションに戻れます．頻繁にリジュームする人は，キーバインドを設定するべきかもしれません．また，プロンプトでユーザが入力する検索語は，一部の正規表現に対応しているので， `^` を付ければ先頭マッチ， `$` 末尾マッチというように制御できます．


## ソーティング設定

少し細かい話になりますが， `ivy` には絞り込み時の振る舞いに影響する重要な変数がいくつか定義されています．基本的に「すべてのコマンドに対する設定（全体設定）」と「個別のコマンドに対する設定」の双方を定義できます．

後述の `prescient.el` を導入する時は，「すべてのコマンドに対する設定」を上書きするため，導入することで振る舞いが変化し，それが気に入らないこともありえます．その場合には，各設定を上書きしないようにするフラグがありますので，とりあえずフラグをOFFの状態で使い始めるのも良さそうです．

| 変数                             | 全体設定        |
|:---------------------------------|:----------------|
| ivy-sort-functions-alist         | ivy-string<     |
| ivy-sort-matches-functions-alist | nil             |
| ivy-re-builders-alist            | ivy&#x2013;regex-plus|

-   ivy-sort-functions-alist
    
    -   絞り込み開始前のリストの並び方を設定します
    -   まだ何もしていない時の選択候補の並びが気になる時は，この変数を設定します．
    -   `prescient.el` を導入し， `ivy-prescient-enable-sorting` などを正しく設定すれば，使用頻度の高い項目を上位に上げるなど，良きに計らってくれます．
-   ivy-sort-matches-functions-alist
    
    -   絞り込み開始後のリストの並び方を設定します
-   ivy-re-builders-alist
    
    -   `ivy--regex`, `regexp-quote`, `ivy--regex-plus`, `ivy--regex-fuzzy`, `ivy--regex-ignore-order` から選べます．独自実装した関数を設定することも可能です．
    -   `ivy--regex-ignore-order` が `helm` の振る舞いに一番近そうです．
    -   `prescient.el` は，「すべてのコマンド」に対して `ivy-prescient-re-builder` を設定します．
        
        -   `ivy-prescient-enable-filtering` が `nil` ならば，設定されません．
    -   絞り込み時の face に影響を与えます．デフォルトの振る舞いが気に入っている場合は， `ivy--regex-plus` を「すべてのコマンド」に対して設定しましょう．
        
        -   ivy&#x2013;regex-ignore-order は，enc org と org enc で結果が同じ．ivy-minibuffer-match-face-1不使用
        -   ivy&#x2013;regex-plus は，enc org と org enc で結果が異なる．ivy-minibuffer-match-face-1使用


# デフォルト機能を便利に使う

まず `counsel.el` にプリセットされているデフォルト機能のうち，いくつかをピックアップして紹介します．それらをカスタマイズすれば，さらに便利に使えるようになります．


## counsel.el にプリセットされているコマンド

counsel.el (0.12.0) には，82個のコマンドが実装されています．頻繁に使うであろうコマンドと，注目のコマンドを抜粋します．完全なリストは，記事の下に示します．

| コマンド              | 機能                         |
|:----------------------|:-----------------------------|
| counsel-minor         | マイナーモードの一覧         |
| counsel-unicode-char  | ユニコード文字の入力補助     |
| counsel-yank-pop      | ペーストした文字列の選択     |
| counsel-mark-ring     | マーク位置の選択             |
| counsel-fzf           | fzf インターフェイス         |
| counsel-ag            | ag インターフェイス          |
| counsel-locate        | locate インターフェイス      |
| counsel-recentf       | recentf インターフェイス     |
| counsel-find-library  | Emacs Lisp ライブラリの選択  |
| counsel-faces         | フェイスの一覧               |
| counsel-colors-emacs  | カラーリスト                 |
| counsel-colors-web    | カラーリスト（Webカラー）    |
| counsel-M-x           | コマンド一覧                 |
| counsel-set-variable  | 変数の一覧と書き換え         |
| counsel-bookmark      | ブックマーク選択             |
| counsel-switch-buffer | バッファリスト（プレビュー付）|
| counsel-ibuffer       | iBuffer インターフェイス     |

`counsel-minor` はマイナーモードを表示して有効・無効化ができるので地味に便利です．ただ見やすさの点では，[manage-minor-mode.el](https://github.com/ShingoFukuyama/manage-minor-mode)がオススメです．


## org.el と共に使う時

`counsel.el` には，[org mode](https://orgmode.org/) ユーザ向けのコマンドとして， `counsel-org-tag`, `counsel-org-tag-agenda`, `counsel-org-goto`, `counsel-org-goto-all`, `counsel-org-file`, `counsel-org-entity`, `counsel-org-capture`, `counsel-org-agenda-headlines` が実装されています．

`counsel-org-tag` は，タグの書き換え時に `ivy` を使いますが，どうやってタグを削除するのかがよくわからないと思います．私も戸惑いましたが，そんな時は， `C-M-j` (ivy-immediate-done) を呼び出せばよいです．

もし， `org-capture` にも `ivy` を使いたい場合は，次の設定を使います．ただ， `org-capture` については，多くのユーザがキーバインドを記憶して呼び出していると思うので，それほど役立たない気もします．

```el
(define-key counsel-mode-map [remap org-capture]  'counsel-org-capture)
```

以下は， `swiper` を使う時に，カーソル位置の単語を自動選択から `org mode` の見出しを除外するためのハックです．

```el
(when (require 'swiper nil t)
  (defun ad:swiper-thing-at-point ()
    "`swiper' with `ivy-thing-at-point'."
    (interactive)
    (let ((thing (if (thing-at-point-looking-at "^\\*+")
                     nil
                   (ivy-thing-at-point))))
      (when (use-region-p)
        (deactivate-mark))
      (swiper thing)))

  (advice-add 'swiper-thing-at-point :override #'ad:swiper-thing-at-point))
```


## dired.el と共に使う時

`ivy-dired-history` を導入すると， dired (`C-x d`) で訪問した場所を記録してソート表示してくれます．さらに，diredにコピー(`C`)と移動(`R`)機能を与えてくれます．ただし，コピーや移動先の表示順位を上げる機能ではないので要注意です．履歴の保存は， `session.el` で明示的に管理しています．一般には `savehist` を使うことが推奨されているようです．

```el
(when (require 'ivy-dired-history nil t)
  (define-key dired-mode-map "," 'dired)
  (with-eval-after-load "session"
    (add-to-list 'session-globals-include 'ivy-dired-history-variable)))
```

なお，最近 `dired` で開いたディレクトリを `M-x dired-recent-open` で一覧できる [dired-recent](https://github.com/Vifon/dired-recent.el) も `ivy` 対応しています．こちらは規定の `dired-recent-directories-file` に履歴が記録されます．


## magit.el と共に使う時

`magit` の branch の選択などが `ivy` インターフェイスになります．後述の `prescient.el` を導入すれば，選択項目を上に上げたり，履歴を保存できます．

```el
(with-eval-after-load "magit"
  (setq magit-completing-read-function 'ivy-completing-read))
```


## eldoc.el と共に使う時

`eldoc` は `eldoc-idle-delay` の値を用いて一定時間が経過したらミニバッファに情報を出します． `helm` は別ウィンドウに情報を出すため衝突しませんが， `ivy` はミニバッファを使うため，高頻度で衝突します．これを回避するために `eldoc-print-after-edit` を有効にしましょう．

`(setq eldoc-print-after-edit t)` でも同様の効果がありますが，いろいろ試した結果，不十分とわかりました（例：Org を開き，counsel-recentf して，org に戻り，treeを移動した時に， `org-eldoc` が反応する）

```el
(with-eval-after-load "eldoc"
  (defun ad:eldoc-message (f &optional string)
    (unless (active-minibuffer-window)
      (funcall f string)))
  (advice-add 'eldoc-message :around #'ad:eldoc-message))
```


## mic-paren.el と共に使う時

`eldoc` と同様に，ミニバッファで衝突します．以下のコードで回避できます．

```el
(with-eval-after-load "mic-paren"
  (defun ad:mic-paren-highlight (f)
    (if (active-minibuffer-window)
        (let ((paren-display-message 'never))
          (funcall f))
      (funcall f)))
  (advice-add 'mic-paren-highlight :around #'ad:mic-paren-highlight))
```


## counsel-find-file を使わない

`counsel-find-file` が `find-file` を置き換えますが，私は素の `find-file` を気に入っているので， `counsel-find-file` を無効化します．

```el
;; completion-in-region-function も一時的にデフォに戻さないと，TAB補完時に
;; ivy が有効化されてしまう．
(defun my-disable-counsel-find-file (&rest args)
  "Disable `counsel-find-file' and use the original `find-file' with ARGS."
  (let ((completing-read-function #'completing-read-default)
        (completion-in-region-function #'completion--in-region))
    (apply #'read-file-name-default args)))
(setq read-file-name-function #'my-disable-counsel-find-file)
;; (counsel-mode 1) を設定しても counsel-find-file が呼ばれないようにする．
(define-key counsel-mode-map [remap find-file]  nil)
```

see <https://emacs.stackexchange.com/questions/45929/disable-ivy-for-find-file>


## find-library のバグを回避

厳密に言えばバグではないのですが，最初に `find-library` を使うと，大量の `./` を表示する問題があります．これは `find-library` ではなく `counsel-find-library` を直接呼べば回避できます．しかし， `M-x` した時のコマンド候補には依然として `find-library` が出てきますので，誤選択の原因になります．

この問題は， `find-library` から `interactive` を取ることで解決します．advice では無意味で，再定義が必要です．

```el
(when (require 'find-func nil t)
  (defun find-library (library)
    "Override the original `find-library' to hide in command list."
    (prog1
        (switch-to-buffer (find-file-noselect (find-library-name library)))
      (run-hooks 'find-function-after-hook))))
```


## counsel-M-x

`helm` や `ivy` を使う大きな目的として，コマンドの絞り込みがあります． `helm` から `ivy` に移行すると， `M-x` の印象がかなり違うので，必要に応じて `helm` 流に合わせて設定して使います．4点ほど設定・カスタマイズします．

まず， `smex` 或いは `amx` を導入して，コマンドの使用履歴を保存・利用します． `amx` がインストールされていると， `smex` よりも優先的に使われます．なお残念ながら `helm-M-x` で培った履歴をそのまま使うのは難しいようです．

```el
(when (require 'smex nil t)
  (setq smex-history-length 35)
  (setq smex-completion-method 'ivy))
```

次に， `M-x ^` とデフォルトで入力される `^` 前方マッチ記号を非表示にします． `ivy-initial-inputs-alist` を設定します．

```el
(setq ivy-initial-inputs-alist
      '((org-agenda-refile . "^")
        (org-capture-refile . "^")
        ;; (counsel-M-x . "^") ;; 削除．必要に応じて他のコマンドも除外する．
        (counsel-describe-function . "^")
        (counsel-describe-variable . "^")
        (Man-completion-table . "^")
        (woman . "^")))
```

さらに， `ivy-re-builders-alist` でスタイルを選びます．私は `ivy--regex-ignore-order` を選択．デフォルトは `ivy--regex-plus` です．

```el
(setf (alist-get 'counsel-M-x ivy-re-builders-alist) #'ivy--regex-ignore-order)
```

最後に，絞り込み開始後のソーティング方法をカスタマイズします．

```el
;; see https://github.com/abo-abo/swiper/issues/1294
(defun ivy--sort-by-len (name candidates)
  "Sort CANDIDATES based on similarity of their length with NAME."
  (let ((name-len (length name))
        (candidates-count (length candidates)))
    (if (< 500 candidates-count)
        candidates
      (seq-sort-by #'length
                   (lambda (a b)
                     (< (abs (- name-len a))
                        (abs (- name-len b))))
                   candidates))))

(setf (alist-get 'counsel-M-x ivy-sort-matches-functions-alist)
      #'ivy--sort-by-len)
```


## counsel-ag

`counsel-ag` は， `grep` と同様に全文検索する `ag` コマンドのインターフェイスを提供します．私はカーソル位置の単語を使って全文検索する頻度が高いので，そのようにハックします．

```el
(defun ad:counsel-ag (f &optional initial-input initial-directory extra-ag-args ag-prompt caller)
  (apply f (or initial-input (ivy-thing-at-point))
         (unless current-prefix-arg
           (or initial-directory default-directory))
         extra-ag-args ag-prompt caller))

(advice-add 'counsel-ag :around #'ad:counsel-ag)
```

さらに，デフォルトではカレントディレクトリ以下の範囲で全文検索しますが，ディレクトリを変えて再検索したいこともあります．検索語を再入力せずディレクトリを変えるために，次の関数を実装して， `M-o r` で呼び出せるようにします．

```el
;; directory を指定して ag やり直し．クエリは再利用する
(defun my-counsel-ag-in-dir (_arg)
  "Search again with new root directory."
  (let ((current-prefix-arg '(4)))
    (counsel-ag ivy-text nil ""))) ;; also disable extra-ag-args

(ivy-add-actions
 'counsel-ag
 '(("r" my-counsel-ag-in-dir "search in directory")))
```

`counsel-cd` が上記の機能を意図しているようですが，想像と少々異なる振る舞いをするので，私は使っていません．

```el
(define-key counsel-ag-map (kbd "C-x C-d") 'counsel-cd)
```

と設定しておけば， `counsel-ag` 使用中に `M-o` せず直接 `C-x C-d` すれば `counsel-cd` を呼べます．


## counsel-fzf

`counsel-ag` の場合と同様に，カーソル位置の単語を使って `counsel-fzf` を起動するようにハックします．

**(2022-08-19)** `default-directory` ではなく， `(funcall counsel-fzf-dir-function)` が使われるように変更．projectile のルートディレクトリから探索されるようになります．

```el
(defun ad:counsel-fzf (f &optional initial-input initial-directory fzf-prompt)
  (apply f (or initial-input
               (if (thing-at-point-looking-at "^\\*+") ;; org heading を除外
                   nil
                 (ivy-thing-at-point)))
         (or initial-directory (funcall counsel-fzf-dir-function))
         fzf-prompt))

(advice-add 'counsel-fzf :around #'ad:counsel-fzf)
```

さらに，検索語を維持したまま探索範囲を変えるための関数を登録します． `counsel-fzf` を利用中に， `M-o r` で発動します．

```el
(defun my-counsel-fzf-in-dir (_arg)
  "Search again with new root directory."
  (counsel-fzf ivy-text
               (read-directory-name
                (concat (car (split-string counsel-fzf-cmd))
                        " in directory: "))))

(ivy-add-actions
 'counsel-fzf
 '(("r" my-counsel-fzf-in-dir "search in directory")))
```


### 検索語を入力してもヒットしないとき counsel-fzf に誘導する（半自動）

`ivy` で検索語を入力すると，ヒットしないことも多いです．入力ミス等なら再入力で済みますが，実は探索範囲を広げたいだけという場合もあります．以下の設定では，検索がヒットしない時に，一定時間が過ぎたら `counsel-fzf` に接続するかを `[y/n]` で問うように設定しています．常に問われると煩雑なので，一つの `ivy` セッションで1回だけ問うようにしています．

`counsel-fzf` に誘導する機能を追加したい場合は， `my-nocand-then-fzf-commands` にコマンドを追加して下さい．

選択候補が無いと確定してから `my-nocand-then-fzf-idle-time` 秒後に `[y/n]` を問うようにカスタマイズできます．

```el
(defcustom my-nocand-then-fzf-commands '(counsel-find-flie
                                         counsel-recentf
                                         counsel-projectile-find-file
                                         counsel-projectile-switch-project)
  "List of commands for applying extension no candidates then `counsel-fzf'."
  :group 'ivy
  :type '(list symbol))

(defcustom my-nocand-then-fzf-idle-time 0.8
  "Idle time for showing prompt."
  :group 'ivy
  :type 'float) ;; 1[s] 無応答の時[y/n]を出す．

(defvar my--nocand-then-fzf t)
(defun my-nocand-then-fzf-reset ()
  (setq my--nocand-then-fzf t))
(defun my-nocand-then-fzf (prompt)
  (when (= ivy--length 0)
    (if (eq (read-char prompt) ?y) ;; y-or-n-p is not applicable
        (ivy-exit-with-action
         (lambda (x)
           (counsel-fzf ivy-text default-directory)))
      (setq my--nocand-then-fzf nil))))
(defun ad:fzf:ivy--insert-prompt ()
  (when (and my--nocand-then-fzf
             (memq (ivy-state-caller ivy-last) my-nocand-then-fzf-commands)
             (= ivy--length 0))
    (let* ((std-props '(front-sticky t rear-nonsticky t field t read-only t))
           (prompt (concat (my-ivy-prompt-prefix) "Switch to Counsel-fzf? [y/n] ")))
      (set-text-properties 0 (length prompt)
                           `(face minibuffer-prompt ,@std-props) prompt)
      (run-with-idle-timer my-nocand-then-fzf-idle-time
                           nil #'my-nocand-then-fzf prompt))))
(advice-add 'ivy--insert-prompt :before #'ad:fzf:ivy--insert-prompt)
(add-hook 'minibuffer-setup-hook #'my-nocand-then-fzf-reset)
(add-hook 'minibuffer-exit-hook #'my-nocand-then-fzf-reset)
```


## counsel-recentf

デフォルトでは "/" からファイルの表示が始まりますが， "~" から始める方が好みなので `counsel-recentf` を上書き設定します．

```el
(defun ad:counsel-recentf ()
  "Find a file on `recentf-list'."
  (interactive)
  (require 'recentf)
  (recentf-mode)
  (ivy-read "Recentf: "
            (progn
              (mapcar #'substring-no-properties recentf-list) ;; no need?
              (mapcar #'abbreviate-file-name recentf-list)) ;; ~/
            :action (lambda (f)
                      (with-ivy-window
                        (find-file f)))
            :require-match t
            :caller 'counsel-recentf))
(advice-add 'counsel-recentf :override #'ad:counsel-recentf)
```

さらに，所望のファイルがヒットしない時に， `M-o z` で `counsel-fzf` に移れるようにします．検索語は引き継がれるので，再入力する手間を省けます．

```el
;; 以下の関数は，counsel-projectile-* と counsel-recentf にぶら下げる．
(defun my-counsel-fzf-in-default-dir (_arg)
  "Search the current directory with fzf."
  (counsel-fzf ivy-text default-directory))

;; add an action for counsel-recentf (M-o z)
(ivy-add-actions
 'counsel-recentf
 '(("z" my-counsel-fzf-in-default-dir "switch to fzf")))
```

なお， `recentf` のリストには，頻繁に自動更新されるが，自分が見ることはほとんどないファイルも含まれます．例えば `bookmarks` です．それらは，使用しないにも関わらずリストの上部に上がってきますので，除外しておくべきです． `ivy` 側の設定で `counsel-find-file-ignore-regexp` を `(regexp-opt '("~/.emacs.d/bookmarks"))` などとすれば回避できますが，それよりも， `recentf` の設定で除外しておくのがよいでしょう．

```el
(custom-set-variables
 '(recentf-exclude
   '(".recentf" "bookmarks" "org-recent-headings.dat" "^/tmp\\.*"
     "^/private\\.*" "^/var/folders\\.*" "/TAGS$")))
```


# パッケージの移行

`helm` で便利に使っていたパッケージを `ivy` のパッケージ，または，フラグメントとして公開されているコードを集めて紹介します．


## helm と ivy-counsel のコマンド対応表

個人的に `helm` で依存していたコマンドの対応表です．現在メインで使用しているコマンドは概ねカバーしています．表中の「X」は，デフォルト実装されているコマンドを表します．

| 機能              |   | Helm                     |   | Ivy/Counsel                  |
|:------------------|:--|:-------------------------|:--|:-----------------------------|
| コマンド選択      | X | helm-M-x                 | X | counsel-M-x                  |
| bookmark.el I/F   | X | helm-bookmark            | X | counsel-bookmark             |
| locate I/F        | X | helm-locate              | X | counsel-locate (1)           |
| バッファ切り替え  | X | helm-buffers-list        | X | counsel-ibuffer              |
| recentf.el I/F    | X | helm-recentf             | X | counsel-recentf              |
| キーバインド表示  |   | helm-descbinds           | X | counsel-descbinds            |
| ag I/F            |   | helm-ag                  | X | counsel-ag                   |
| 複数単語検索      |   | helm-swoop               | X | swiper                       |
| bm.el I/F         |   | helm-bm                  |   | counsel-bm (2)               |
| projectile.el I/F |   | helm-projectile          |   | counsel-projectile (3)       |
| 単語の辞書記録    |   | flyspell-correct-helm    |   | flyspell-correct-ivy (4)     |
| heading の選択    |   | org-recent-headings-helm |   | org-recent-headings-ivy (5)  |
| global I/F        |   | helm-gtags               |   | counsel-gtags (6)            |
| dired 履歴        |   | helm-dired-history       |   | ivy-dired-history (7)        |
| selected.el I/F   |   | helm-selected            |   | counsel-selected (8)         |
| flycheck.el I/F   |   | helm-flycheck            |   | counsel-flycheck (9)         |
| pass I/F          |   | helm-pass                |   | ivy-pass (10)                |
| emms.el I/F       |   | helm-emms                |   | nil                          |
| emmet I/F         |   | helm-emmet               |   | nil                          |

(1) [counsel-locate](https://oremacs.com/swiper/#example---counsel-locate)
(2) [counsel-bm](https://www.reddit.com/r/emacs/comments/700xck/ivy_with_bmel_bookmark_manager/) (fragment)
(3) [counsel-projectile](https://github.com/ericdanan/counsel-projectile)
(4) [flyspell-correct-ivy](https://github.com/d12frosted/flyspell-correct)
(5) [org-recent-headings-ivy](https://github.com/alphapapa/org-recent-headings)
(6) [counsel-gtags](https://github.com/syohex/emacs-counsel-gtags)
(7) [ivy-dired-history](https://github.com/jixiuf/ivy-dired-history)
(8) [counsel-selected](https://github.com/takaxp/counsel-selected)
(9) [counsel-flycheck](https://github.com/nathankot/dotemacs/blob/master/init.el#L709) (fragment)
(10) [ivy-pass](https://github.com/ecraven/ivy-pass)


## counsel-flycheck (fragment)

<https://github.com/nathankot/dotemacs/blob/master/init.el#L709> にありますので．拝借しました．関数名を変更して， `caller` を追加しています．

```el
(defvar counsel-flycheck-history nil
  "History for `counsel-flycheck'")

(defun counsel-flycheck ()
  (interactive)
  (if (not (bound-and-true-p flycheck-mode))
      (message "Flycheck mode is not available or enabled")
    (ivy-read "Error: "
              (let ((source-buffer (current-buffer)))
                (with-current-buffer
                    (or (get-buffer flycheck-error-list-buffer)
                        (progn
                          (with-current-buffer
                              (get-buffer-create flycheck-error-list-buffer)
                            (flycheck-error-list-mode)
                            (current-buffer))))
                  (flycheck-error-list-set-source source-buffer)
                  (flycheck-error-list-reset-filter)
                  (revert-buffer t t t)
                  (split-string (buffer-string) "\n" t " *")))
              :action (lambda (s &rest _)
                        (-when-let* ( (error (get-text-property 0 'tabulated-list-id s))
                                      (pos (flycheck-error-pos error)) )
                          (goto-char (flycheck-error-pos error))))
              :history 'counsel-flycheck-history
              :caller 'counsel-flycheck)))
```


## counsel-bm (fragment)

<https://www.reddit.com/r/emacs/comments/700xck/ivy_with_bmel_bookmark_manager/> に公開されているコードをアレンジしました． `ivy` とは無関係ですが， `<f10>` で `bm.el` の `bm-toggle` を呼び出し， `C-<f10>` で次のブックマークに移動するように設定しています．詳細は， [Configurations for GNU Emacs::4.9 [bm.el] カーソル位置をブックマークして追う](https://takaxp.github.io/init.html#orgd5942d14) を参照ください．

```el
(with-eval-after-load "bm"
  (defun counsel-bm-get-list (bookmark-overlays)
    (-map (lambda (bm)
            (with-current-buffer (overlay-buffer bm)
              (let* ((line (replace-regexp-in-string
                            "\n$" ""
                            (buffer-substring (overlay-start bm)
                                              (overlay-end bm))))
                     ;; line numbers start on 1
                     (line-num
                      (+ 1 (count-lines (point-min) (overlay-start bm))))
                     (name (format "%s:%d - %s" (buffer-name) line-num line)))
                `(,name . ,bm))))
          bookmark-overlays))

  (defun counsel-bm ()
    (interactive)
    (let* ((bm-list (counsel-bm-get-list (bm-overlays-lifo-order t)))
           (bm-hash-table (make-hash-table :test 'equal))
           (search-list (-map (lambda (bm) (car bm)) bm-list)))
      (-each bm-list (lambda (bm)
                       (puthash (car bm) (cdr bm) bm-hash-table)
                       ))
      (ivy-read "Find bookmark(bm.el): "
                search-list
                :require-match t
                :keymap counsel-describe-map
                :action (lambda (chosen)
                          (let ((bookmark (gethash chosen bm-hash-table)))
                            (switch-to-buffer (overlay-buffer bookmark))
                            (bm-goto bookmark)
                            ))
                :sort t)))

  (global-set-key (kbd "<S-f10>") 'counsel-bm))
```


## counsel-world-clock.el

[counsel-world-clock](https://github.com/kchenphy/counsel-world-clock) は，タイムゾーンから時刻と時差を調べるツールです．ビルトインの `display-time-world` でも似たことができますが， `ivy` でタイムゾーンを絞り込めるので圧倒的に便利です．

デフォルトではソートされないので，[prescient.el](https://github.com/raxod502/prescient.el) を導入して，直近の選択を上位に移すソーティングと履歴保存を有効にしましょう．

```el
(with-eval-after-load "ivy"
  ;; package 経由のインストールなら，M-x counsel-world-clock ですぐ使える．
  (require 'counsel-world-clock nil t))
```


## counsel-selected.el

拙作の [helm-selected](https://github.com/takaxp/helm-selected) を `ivy` に移植しました．どちらかといえば，絞り込みしたいと言うより，領域選択から `l` 押下で，メニューを表示し，設定項目を思い出すために使います．

```el
(when (require 'counsel-selected nil t)
  (define-key selected-keymap (kbd "l") 'counsel-selected))
```


## counsel-osx-app.el

`counse.el` には Linuxのアプリケーションを呼び出すためのインターフェイスである `counsel-linux-app` が実装されています． macOS用のコマンドは [counsel-osx-app](https://github.com/d12frosted/counsel-osx-app) に公開されています．以前はOS側のランチャーを呼び出して使っていましたが，このコマンドのおかげで，Emacs から直接アプリケーションを呼び出せるので，かなり重宝しています．

`counsel-osx-app-location` を正しく設定すれば，サブディレクトリに含まれるアプリケーションもリストに入れてくれます．

```el
(global-set-key (kbd "C-M-1") 'counsel-osx-app)
(with-eval-after-load "counsel-osx-app"
  (custom-set-variables
   '(counsel-osx-app-location
     '("/Applications" "/Applications/Utilities"
       "/Applications/Microsoft Remote Desktop.localized"))))
```


## counsel-projectile.el

`project.el` に `ivy` インターフェイスを提供するパッケージです． `projectile-find-file` は `counsel-git` に近い気もします．下記の `my-counsel-fzf-in-default-dir` は， `counsel-recentf` に紐付けたアクションと同じです．

```el
;; 以下の関数は，counsel-projectile-* と counsel-recentf にぶら下げる．
(defun my-counsel-fzf-in-default-dir (_arg)
  "Search the current directory with fzf."
  (counsel-fzf ivy-text default-directory))

(with-eval-after-load "projectile"
  (when (require 'counsel-projectile nil t)
    ;; M-o z で fzf を呼び出せる．
    ;; https://twitter.com/takaxp/status/1134481340458360832
    ;; あれ，これなんで動いてるの？関数に add-to-list できるの？
    ;; 以下の2つの関数は，動的に生成される defcustom で規定される．すごい．
    (add-to-list 'counsel-projectile-switch-project-action
                 '("z" my-counsel-fzf-in-default-dir
                   "switch to fzf") t)
    (add-to-list 'counsel-projectile-find-file-action
                 '("z" my-counsel-fzf-in-default-dir
                   "switch to fzf") t)

    (setq projectile-completion-system 'ivy)
    (setq counsel-projectile-sort-files t) ;; 当該プロジェクト内リストをソート
    (setq counsel-projectile-sort-projects t) ;; プロジェクトリストをソート
    (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
    (counsel-projectile-mode 1)))
```


## counsel-gtags.el

安全安心の @syohex製 <https://github.com/syohex/emacs-counsel-gtags>

```el
(when (require 'counsel-gtags nil t)
  (setq counsel-gtags-update-interval-second 10)
  (dolist (hook '(c-mode-hook c++-mode-hook))
    (add-hook hook 'counsel-gtags-mode)))
```


## flyspell-correct-ivy.el

意外と使用頻度が高い `flyspell` の `ivy` インターフェイスです．当初，新しい単語を追加する `save` を helm のように，[選択リストに含める方法](https://github.com/d12frosted/flyspell-correct/issues/10)を選択しましたが， `ivy` を使い込んでいくうちに， `M-o` で追加メニューを表示する `ivy` のお作法になれる方が生産的と気づきました．

```el
(when (require 'flyspell-correct-ivy nil t)
  (setq flyspell-correct-interface '#'flyspell-correct-ivy)
  (global-set-key (kbd "<f7>") 'flyspell-correct-word-generic))
```


## ivy-pass.el

特に設定はしていませんが，基本的にバグっている気がします．私の環境だけかもしれません&#x2026;

see <https://github.com/ecraven/ivy-pass>


## org-recent-headings-ivy (in org-recent-headings.el)

最近使用した `org mode` の見出しを **正確に** 記録してくれる `org-recent-headings.el` の `ivy` インターフェイスです．とても便利でオススメなのですが，残念ながら，org の HEAD では壊れて使えないため，少し前の version で使ってください．動作確認ができたら，記事を後日更新します．

なお，現状では履歴のcleanup機能が無いので， `~/.emacs.d/org-recent-headings.dat` は端末間で共有しない方がよいと思います．

```el
(with-eval-after-load "ivy"
  ;; デフォルトだと `ivy-string<' が使われてしまい，使用履歴が反映されない．
  ;; つまり， recent-headings.dat に記録された順が反映されない．
  (add-to-list 'ivy-sort-functions-alist
               '(org-recent-headings-ivy . nil))

  ;; Originally located in org-recent-headings.el.
  (defun org-recent-headings-ivy ()
    "Choose from recent Org headings with Ivy."
    (interactive)
    (org-recent-headings :completing-read-fn #'ivy-completing-read)))

;; for Ivy interface
(global-set-key (kbd "C-c f r") 'org-recent-headings-ivy)

(with-eval-after-load "org-recent-headings"
  (custom-set-variables
   '(org-recent-headings-save-file "~/.emacs.d/org-recent-headings.dat")
   '(org-recent-headings-show-entry-function
     'org-recent-headings--show-entry-direct)
   '(org-recent-headings-advise-functions
     '(org-agenda-goto
       org-agenda-show
       org-agenda-show-mouse
       org-show-entry ;; having a bug, so just disabled
       org-reveal
       org-refile
       org-tree-to-indirect-buffer
       org-bookmark-jump))))

(defun ad:org-recent-headings-activate ()
  (interactive)
  (when (and (require 'dash-functional nil t) ;; FIXME
             (require 'org-recent-headings nil t))
    (org-recent-headings-mode)
    (advice-remove 'org-recent-headings-ivy
                   #'ad:org-recent-headings-activate)))
(advice-add 'org-recent-headings-ivy :before
            #'ad:org-recent-headings-activate)
```


# prescient.el

`ivy-prescient.el` を導入すると，各 `ivy` インターフェイスの使用履歴が記録され，最近選択した項目を上位に出してくれるようになります．ただし，すべてのコマンドが影響下に入るわけではなく， `ivy-read` に正しく `:caller` が設定されている場合に限られます．また， `:sort nil` または `:sort` 設定が存在しない `ivy-read` の場合は， `ivy-prescient-sort-commands` に対象コマンドを追加することで， `ivy-prescient.el` の影響下に置くことができます．

もう一つの機能として，イニシャル入力が可能になります．例えば `counsel-M-x` で `M-x fap` と入力すると， `find-file-at-point` が最上位で表示されます． `counsel-recentf` などでは，サブディレクトリの先頭の頭文字を入力すると，当該の情報が上位にきます．慣れれば極めて高速に絞り込みできるようになります．ただ，すべてのコマンドについてイニシャル入力を有効にすると，現状では `face` を思うように制御できないので， `ivy-re-builders-alist` を設定して，イニシャル入力を有効化するコマンドを限定することをオススメします．

イニシャル入力の設定周りがややこしいと感じる場合は，下記設定の2箇所の `ivy-re-builders-alist` に関わる設定を使わず，代わりに `(setq ivy-prescient-enable-filtering nil)` を設定すればうまく行きます．このように設定しても，使用履歴の保存には影響がありません．

なお， `prescient.el` の内部アルゴリズムは `ivy` 以外にも `company-mode` でも使えるようです．

```el
(when (require 'prescient nil t)

  ;; ivy インターフェイスでコマンドを実行するたびに，キャッシュをファイル保存
  (setq prescient-aggressive-file-save t)
  ;; ファイルの保存先
  (setq prescient-save-file
        (expand-file-name "~/.emacs.d/prescient-save.el"))
  ;; アクティベート
  (prescient-persist-mode 1))

(when (require 'ivy-prescient nil t)
  ;; =ivy= の face 情報を引き継ぐ（ただし，完全ではない印象）
  (setq ivy-prescient-retain-classic-highlighting t)
  ;; コマンドを追加
  (dolist (command '(counsel-world-clock ;; Merged!
                     counsel-app)) ;; add :caller
    (add-to-list 'ivy-prescient-sort-commands command))

  ;; フィルタの影響範囲を限定する．以下の3つは順番が重要．
  ;; (1) マイナーモードの有効化
  (ivy-prescient-mode 1)
  ;; (2) =counsel-M-x= をイニシャル入力対応にする
  (setf (alist-get 'counsel-M-x ivy-re-builders-alist)
        #'ivy-prescient-re-builder)
  ;; (3) デフォルトのイニシャル入力を上書きする
  (setf (alist-get t ivy-re-builders-alist) #'ivy--regex-ignore-order))
```


# 視覚化

`ivy` の良いところは，アイコンを導入して視覚的な見やすさを簡単に向上できるところです．プロンプト，候補リストにアイコンを導入すると，選択対象がどのカテゴリのファイルなのかをファイル名や拡張子ではなく，アイコンで視覚的に把握できるようになります．


## プロンプトを改良

プロンプトは，選択候補の数とコマンド名の組み合わせで構成されます． `ivy-count-format` をカスタマイズすれば，現在のカーソル位置が，候補リストの何番目に位置するかも表示できます．

```el
(setq ivy-count-format "(%d/%d) ")
```

さらに，以下のようなハックを施すと，プロンプトの前に任意の情報を書き込む事ができます．例えば，プロンプトの上部に一行空行を入れて，さらに，プロンプトの直前にアイコンを入れることも可能です．モードラインを使わない派にオススメの設定です．

ただハックの方法としては `ivy--insert-prompt` を advice で override する必要があるので，公式にPR出そうと考えています．

```el
(with-eval-after-load "ivy"
  (defvar my-ivy-prompt-prefix t)
  (defun my-toggle-ivy-prompt-prefix ()
    "Toggle showing a header line before the ivy prompt."
    (interactive)
    (setq my-ivy-prompt-prefix (not my-ivy-prompt-prefix)))

  (defun my-ivy-prompt-prefix ()
    "Return a header line for the ivy prompt."
    (when my-ivy-prompt-prefix
      (if window-system
          (format "%s\n%s "
                  (make-string (frame-width) ?\x5F) ;; "__"
                  (all-the-icons-faicon "sort-amount-asc")) ;; ""
        (format "%s\n" (make-string (1- (frame-width)) ?\x2D))))) ;; "--"

  (defun ivy--insert-prompt ()
    "Update the prompt according to `ivy--prompt'."
    (when (setq ivy--prompt (ivy-prompt))
      (unless (memq this-command '(ivy-done ivy-alt-done ivy-partial-or-done
                                            counsel-find-symbol))
        (setq ivy--prompt-extra ""))
      (let (head tail)
        (if (string-match "\\(.*?\\)\\(:? ?\\)\\'" ivy--prompt)
            (progn
              (setq head (match-string 1 ivy--prompt))
              (setq tail (match-string 2 ivy--prompt)))
          (setq head ivy--prompt)
          (setq tail ""))
        (let ((inhibit-read-only t)
              (std-props '(front-sticky t rear-nonsticky t field t read-only t))
              (n-str
               (concat
                (if (and (bound-and-true-p minibuffer-depth-indicate-mode)
                         (> (minibuffer-depth) 1))
                    (format "[%d] " (minibuffer-depth))
                  "")
                (concat
                 (if (string-match "%d.*%d" ivy-count-format)
                     (format head
                             (1+ ivy--index)
                             (or (and (ivy-state-dynamic-collection ivy-last)
                                      ivy--full-length)
                                 ivy--length))
                   (format head
                           (or (and (ivy-state-dynamic-collection ivy-last)
                                    ivy--full-length)
                               ivy--length)))
                 ivy--prompt-extra
                 tail)))
              (d-str (if ivy--directory
                         (abbreviate-file-name ivy--directory)
                       "")))
          (save-excursion
            (goto-char (point-min))
            (delete-region (point-min) (minibuffer-prompt-end))
            (let ((len-n (length n-str))
                  (len-d (length d-str))
                  (ww (window-width)))
              (setq n-str
                    (cond ((> (+ len-n len-d) ww)
                           (concat n-str "\n" d-str "\n"))
                          ((> (+ len-n len-d (length ivy-text)) ww)
                           (concat n-str d-str "\n"))
                          (t
                           (concat n-str d-str)))))
            ;; ADDED
            (when my-ivy-prompt-prefix
              (setq n-str (concat (my-ivy-prompt-prefix) n-str)))
            ;;
            (when ivy-add-newline-after-prompt
              (setq n-str (concat n-str "\n")))
            (let ((regex (format "\\([^\n]\\{%d\\}\\)[^\n]" (window-width))))
              (while (string-match regex n-str)
                (setq n-str (replace-match
                             (concat (match-string 1 n-str) "\n")
                             nil t n-str 1))))
            (set-text-properties 0 (length n-str)
                                 `(face minibuffer-prompt ,@std-props)
                                 n-str)
            (setq n-str (funcall ivy-set-prompt-text-properties-function
                                 n-str std-props))
            (insert n-str))
          ;; Mark prompt as selected if the user moves there or it is the only
          ;; option left.  Since the user input stays put, we have to manually
          ;; remove the face as well.
          (when ivy--use-selectable-prompt
            (if (= ivy--index -1)
                (ivy-add-face-text-property
                 (minibuffer-prompt-end) (line-end-position) 'ivy-prompt-match)
              (remove-list-of-text-properties
               (minibuffer-prompt-end) (line-end-position) '(face))))
          ;; get out of the prompt area
          (constrain-to-field nil (point-max))))))
  (advice-add 'ivy--insert-prompt :override #'ad:ivy--insert-prompt))
```


## 現在の選択候補をわかりやすくする

`ivy-format-functions-alist` にカスタマイズした関数を追加すれば，プロンプトの下部に位置するカーソル行の表示を変更できます． `all-the-icons` を導入して，好みのアイコンでカーソル行のある選択候補を目立たせましょう．

```el
(defface my-ivy-arrow-visible
  '((((class color) (background light)) :foreground "orange")
    (((class color) (background dark)) :foreground "#EE6363"))
  "Face used by Ivy for highlighting the arrow.")

(defface my-ivy-arrow-invisible
  '((((class color) (background light)) :foreground "#FFFFFF")
    (((class color) (background dark)) :foreground "#31343F"))
  "Face used by Ivy for highlighting the invisible arrow.")

;; デフォルトを削除
(when (require 'counsel nil t)
  (delete '(t . ivy-format-function-default) ivy-format-functions-alist))

(if window-system
    (when (require 'all-the-icons nil t)
      (defun my-ivy-format-function-arrow (cands)
        "Transform CANDS into a string for minibuffer."
        (ivy--format-function-generic
         (lambda (str)
           (concat (all-the-icons-faicon
                    "hand-o-right"
                    :v-adjust -0.2 :face 'my-ivy-arrow-visible)
                   " " (ivy--add-face str 'ivy-current-match)))
         (lambda (str)
           (concat (all-the-icons-faicon
                    "hand-o-right" :face 'my-ivy-arrow-invisible) " " str))
         cands
         "\n"))
      (add-to-list 'ivy-format-functions-alist
                   '(t . my-ivy-format-function-arrow) t))
  (add-to-list 'ivy-format-functions-alist
               '(t . my-ivy-format-function-arrow-line) t))
```

**(2022-08-27)** `ivy-format-functions-alist` を `setq` で設定すると， `counsel.el` が先に読み込まれている場合に，そこで設定された値を破壊してしまうので，デフォルトの設定を削除して(`delete`)，新しい設定をリストの最後に追加する(`add-to-list`)方法に変えました． `add-to-list` は，第3引数に `t` を与えることで `append` になります． `append` にしないと，上記のコードで指定した関数が常に呼ばれてしまいます．


## ivy-rich.el

ミニバッファに候補リストを表示する `ivy` は，大きな画面で横方向に複数のウィンドウを表示する状況で，画面上に無駄なスペースが多く生じます． `ivy-rich` を導入すると，アイコンが追加され，さらに追加の情報が空きスペースに記述されるようになります．

私のように普段は80桁縛りで Emacs を使う人には相性が悪いかもしれません．

```el
(when (require 'ivy-rich nil t)
  (ivy-rich-mode 1))
```


## all-the-icons-ivy.el

-   <https://github.com/asok/all-the-icons-ivy>

`ivy` インターフェイスにアイコンを追加するパッケージです． `all-the-icons.el` が少々導入の難しいパッケージなのですが，一度設定がうまくいけば，殺伐とした Emacs バッファに安らぎがもたらされます．

いくつかのコマンドは，すでにアイコンを出すように設定されています．アイコンが表示されないコマンドを見つけたら， `all-the-icons-ivy-buffer-commands` と `all-the-icons-ivy-file-commands` にコマンドを追加すれば，アイコンが表示されるようになります．

なお，試した感じでは， `all-the-icons-ivy` と `ivy-rich` を併用するのは厳しそうです．

```el
(when (require 'all-the-icons-ivy nil t)
  (dolist (command '(counsel-projectile-switch-project
                     counsel-ibuffer))
    (add-to-list 'all-the-icons-ivy-buffer-commands command))
  (all-the-icons-ivy-setup))
```

`all-the-icons` を導入した結果，候補リストの行頭が揃わない場合には，次の設定が効果を発揮するかもしれません．このあたりは難しく，まだ制御しきれていません．

```el
(with-eval-after-load "all-the-icons-ivy"
      (defvar my-tab-width tab-width)
      (defun my-tab-width-2 () (setq tab-width 2))
      (defun my-tab-width-1 () (setq tab-width 1))
      (defun my-tab-width-8 () (setq tab-width 8))
      (defun my-tab-width-original ()
        (setq tab-width my-tab-width))
      (add-hook 'minibuffer-setup-hook #'my-tab-width-2)
      (add-hook 'minibuffer-exit-hook #'my-tab-width-original))
```


## ivy-posframe.el

`ivy-posframe` を使うと，通常はミニバッファに表示されるリストを任意の位置に出すことができます．私自身，まだ使いこなせていないので今後の課題です．以下は， `counsel-M-x` でコマンドリストを表示するときだけカーソル位置にフレームを出す設定です．なお，当初 `ivy-posframe` はマイナーモード化されておらず不便でしたが， `@conao3` 氏の[尽力](https://github.com/tumashu/ivy-posframe/pull/43)で現在は `(ivy-posframe-mode 1)`, `(ivy-posframe-mode -1)` で ON/OFFできます．

```el
(with-eval-after-load "ivy-posframe"
  (setq ivy-posframe-display-functions-alist
        '((counsel-M-x . ivy-posframe-display-at-point)
          (t           . ivy-posframe-display)))
  (ivy-posframe-mode 1))
```


# 分析


## 読み込みコスト

`ivy.el` と `helm.el` の読み込み時間の計測を比較しました．それぞれ依存関係のパッケージの読み込みを含みます．計測は私の貧弱環境です．3回平均の単位は[ms]です．

思った以上に `ivy.el` の読み込みに時間を要しています． `ivy.el` 自体は約140[ms] ですが，さらに `ffap.el` の読み込みで約120[ms]程度消費します．

|             |   1 |   2 |   3 | Ave. |
|:------------|----:|----:|----:|-----:|
| helm.el     | 201 | 207 | 175 |  194 |
| ivy.el      | 430 | 412 | 403 |  415 |
| counsel.el  | 101 | 102 | 121 |  108 |
| ivy+counsel | 531 | 514 | 524 |  523 |


## パッケージサイズ

`ivy` と `helm` でMALPA経由でインストールする場合のパッケージサイズを比較します．バイトコンパイル済みです．単位は[MB]です．

結果， `ivy`  (1.36[MB]) と `helm` (3.27[MB]) で `ivy` が軽量のようです．ただし， `ivy` は，いくつかのコマンドが init.el 側に記述されています．フラグメントなのでサイズ増加のインパクトは小さいです．

| helm                                 |  size |
|:-------------------------------------|------:|
| flyspell-correct-helm-20181205.1932/ | 0.016 |
| helm-20190726.943/                   |   1.9 |
| helm-ag-20170209.1545/               |   0.1 |
| helm-bm-20160321.1331/               | 0.024 |
| helm-core-20190726.1001/             | 0.766 |
| helm-descbinds-20190501.935/         | 0.032 |
| helm-dired-history-20170524.1046/    | 0.020 |
| helm-flycheck-20160710.829/          | 0.024 |
| helm-gtags-20170116.529/             | 0.116 |
| helm-pass-20190315.1335/             | 0.020 |
| helm-projectile-20190721.842/        | 0.112 |
| helm-swoop-20180215.1154/            | 0.136 |
| SUM                                  |  3.26 |

| ivy                                |  size |
|:-----------------------------------|------:|
| flyspell-correct-ivy-20181205.1932 | 0.016 |
| all-the-icons-ivy-20190508.1803/   | 0.024 |
| ivy-20190726.2134/                 | 0.492 |
| ivy-dired-history-20170626.556/    | 0.028 |
| ivy-pass-20170812.1955/            | 0.016 |
| ivy-rich-20190707.107/             | 0.052 |
| counsel-20190726.1745/             | 0.440 |
| counsel-projectile-20190724.1903/  | 0.144 |
| counsel-world-clock-20190709.2211/ | 0.036 |
| swiper-20190726.1746/              | 0.116 |
| SUM                                |  1.37 |


## helm と共存の可能性

いくつかのコマンドを試しましたが，基本的に共存できる印象です．ただ基本的には同一のインターフェイスを使う方が効率が上がると思いますので，どうしても移行困難なコマンドに限定して併用するのが良さそうです．例えば， `org-trello` が `helm` に依存しているので，私はしばらく `helm` 環境を残すことになりそうです．

なお， `org-recent-headings` をインストールしていると共存が厳しくなります．これはバイトコンパイルせずに使うことで回避できます．


## counsel.el にプリセットされている ivy-read 一覧

実装されているコマンド一覧です．

| Command                            | DOC STRING                                                   |
|:-----------------------------------|:-------------------------------------------------------------|
| counsel-el                         | Elisp completion at point.                                   |
| counsel-cl                         | Common Lisp completion at point.                             |
| counsel-jedi                       | Python completion at point.                                  |
| counsel-clj                        | Clojure completion at point.                                 |
| counsel-company                    | Complete using \`company-candidates'.                        |
| counsel-irony                      | Inline C/C++ completion using Irony.                         |
| counsel-describe-variable          | Forward to \`describe-variable'.                             |
| counsel-describe-function          | Forward to \`describe-function'.                             |
| counsel-set-variable               | Set a variable SYM, with completion.                         |
| counsel-apropos                    | Show all matching symbols.                                   |
| counsel-info-lookup-symbol         | Forward SYMBOL to \`info-lookup-symbol'                      |
| counsel-M-x                        | Ivy version of \`execute-extended-command'.                  |
| counsel-command-history            | Show the history of commands.                                |
| counsel-load-library               | Load a selected the Emacs Lisp library.                      |
| counsel-find-library               | Visit a selected the Emacs Lisp library.                     |
| counsel-load-theme                 | Forward to \`load-theme'.                                    |
| counsel-descbinds                  | Show a list of all defined keys and their definitions.       |
| counsel-describe-face              | Completion for \`describe-face'.                             |
| counsel-faces                      | Complete faces with preview.                                 |
| counsel-git                        | Find file in the current Git repository.                     |
| counsel-git-grep                   | Grep for a string in the current Git repository.             |
| counsel-git-stash                  | Search through all available git stashes.                    |
| counsel-git-change-worktree        | Find the file corresponding to the current buffer            |
| counsel-git-checkout               | Call the \\"git checkout\\" command.                         |
| counsel-git-log                    | Call the \\"git log &#x2013;grep\\" shell command.           |
| counsel-find-file                  | Forward to \`find-file'.                                     |
| counsel-dired                      | Forward to \`dired'.                                         |
| counsel-recentf                    | Find a file on \`recentf-list'.                              |
| counsel-bookmark                   | Forward to \`bookmark-jump'                                  |
| counsel-bookmarked-directory       | Ivy interface for bookmarked directories.                    |
| counsel-file-register              | Search file in register.                                     |
| counsel-locate-action-extern       | Pass X to \`xdg-open'                                        |
| counsel-locate                     | Call the \\"locate\\" shell command.                         |
| counsel-fzf                        | Open a file using the fzf shell command.                     |
| counsel-dpkg                       | Call the \\"dpkg\\" shell command.                           |
| counsel-rpm                        | Call the \\"rpm\\" shell command.                            |
| counsel-file-jump                  | Jump to a file below the current directory.                  |
| counsel-dired-jump                 | Jump to a directory (see \`dired-jump')                      |
| counsel-ag                         | Grep for a string in the current directory using ag.         |
| counsel-pt                         | Grep for a string in the current directory using pt.         |
| counsel-ack                        | Grep for a string in the current directory using ack.        |
| counsel-rg                         | Grep for a string in the current directory using rg.         |
| counsel-grep                       | Grep for a string in the file visited by the current buffer  |
| counsel-grep-backward              | Grep for a string similar to \`swiper-backward'              |
| counsel-grep-or-swiper             | Call \`swiper' for small buffers and \`counsel-grep'         |
| counsel-grep-or-swiper-backward    | Call \`swiper-backward'  and \`counsel-grep-backward'        |
| counsel-recoll                     | Search for a string in the recoll database.                  |
| counsel-org-tag                    | Add or remove tags in \`org-mode'.                           |
| counsel-org-tag-agenda             | Set tags for the current agenda item.                        |
| counsel-org-goto                   | "Jump to an outline heading with completion."                |
| counsel-org-goto-all               | Go to a different location in any org file.                  |
| counsel-org-file                   | Browse all attachments for current Org file.                 |
| counsel-org-entity                 | Complete Org entities using Ivy.                             |
| counsel-org-capture                | Capture something.                                           |
| counsel-org-agenda-headlines       | Choose from headers of \`org-mode' files in the agenda.      |
| counsel-mark-ring                  | Browse \`mark-ring' interactively.                           |
| counsel-package                    | Install or delete packages.                                  |
| counsel-tmm                        | Text-mode emulation of looking and choosing from a menubar.  |
| counsel-yank-pop                   | Ivy replacement for \`yank-pop'.                             |
| counsel-register                   | Interactively choose a register.                             |
| counsel-evil-registers             | Ivy replacement for \`evil-show-registers'.                  |
| counsel-imenu                      | Jump to a buffer position indexed by imenu.                  |
| counsel-list-processes             | Offer completion for \`process-list'.                        |
| counsel-minibuffer-history         | "Browse minibuffer history."                                 |
| counsel-esh-history                | "Browse Eshell history."                                     |
| counsel-shell-history              | "Browse shell history."                                      |
| counsel-hydra-heads                | Call a head of the current/last hydra.                       |
| counsel-semantic                   | "Jump to a semantic tag in the current buffer."              |
| counsel-semantic-or-imenu          |                                                              |
| counsel-ibuffer                    | Use ibuffer to switch to another buffer.                     |
| counsel-switch-to-shell-buffer     | Switch to a shell buffer, or create one.                     |
| counsel-unicode-char               | Insert COUNT copies of a Unicode character at point.         |
| counsel-colors-emacs               | "Show a list of all supported colors for a particular frame. |
| counsel-colors-web                 | Show a list of all W3C web colors for use in CSS.            |
| counsel-rhythmbox                  | Choose a song from the Rhythmbox library to play or enqueue. |
| counsel-linux-app                  | "Launch a Linux desktop application, similar to Alt-<F2>.    |
| counsel-wmctrl                     | "Select a desktop window using wmctrl."                      |
| counsel-switch-buffer              | Switch to another buffer.                                    |
| counsel-switch-buffer-other-window | Switch to another buffer in another window.                  |
| counsel-compile                    | Call \`compile' completing with smart suggestions            |
| counsel-compile-env                | Update \`counsel-compile-env' interactively.                 |
| counsel-minor                      | Enable or disable minor mode.                                |

82個/85個．

-   counsel&#x2013;org-get-tags
-   counsel-outline (counsel-org-goto)
-   counsel-mode (minor-mode)


# まとめ

この記事では（長々と） `helm` から `ivy` に移行した時に得られた知見と設定について解説しました．幸いながら，私の `helm` 利用レベルはそれほど高くなく，移行して全く後悔はないですが， `helm` への依存度がより高い人の場合は，色々と気に入らない点があるかもしれません．また， `org-trello.el` 等， `helm` 依存で `ivy` が使えないパッケージもまだあるので，移行に慎重な人は，一度自分が依存するパッケージの `ivy` インターフェイスが存在するかを確認してみるのをオススメします．それ以外の人は，もう「えいや！」で移行しても公開しないと思います．

**いかがでしたか？**


# References

-   [Ivy User Manual](https://oremacs.com/swiper/)
-   [Introducing ivy-mode · (or emacs](https://oremacs.com/2015/04/16/ivy-mode/)
-   <https://speakerdeck.com/takaxp/counsel>
-   <https://oremacs.com/swiper/#faces>
-   <https://writequit.org/denver-emacs/presentations/2017-04-11-ivy.html>
-   <https://blog.jft.rocks/emacs/helm-to-ivy.html>
-   <https://qiita.com/Ladicle/items/feb5f9dce9adf89652cf>
-   <https://github.com/abo-abo/swiper/blob/596461e1ff09431eb417877a9870e53c452e1b62/doc/Changelog.org>
-   <https://github.com/abo-abo/swiper/issues/924>
-   <https://github.com/abo-abo/swiper/issues/309>
-   <https://www.reddit.com/r/emacs/comments/7vcrwo/helm_vs_ivy_what_are_the_differences_what_are_the/>
-   <https://www.reddit.com/r/emacs/comments/51lqn9/helm_or_ivy/>