2021-08-04

ivy-switch-bufferでブックマーク内のファイルをブックマーク名で検索できるようにする

Ivyでは ivy-use-virtual-bufferst にすると ivy-switch-buffer (C-x b) で(recentfや)ブックマーク内のファイルが選べるようになりますが、候補として登録されるのはあくまでファイル名(ivy-virtual-abbreviate を設定するとディレクトリパスも含めることは出来る)だけでブックマーク名は登録されません。なのでいくら分かりやすいブックマーク名を付けていても ivy-switch-buffer でそれを元にファイルを検索することは出来ません。なのでそれを出来るようにしてみました。

(require 'ivy)

(defun my-ivy-bookmark-name-filename-list ()
  "Return bookmark (name . filename) list as virtual buffer format."
  (delq nil
        (mapcar (lambda (record)
                  (when record
                    (let ((name (bookmark-name-from-full-record record))
                          (filename (bookmark-get-filename record)))
                      (when (and name filename)
                        (cons
                         (propertize
                          (format "*bookmark:%s" name)
                          'face 'ivy-virtual)
                         filename)))))
                bookmark-alist)))

(defun my-ivy-bookmark-append-advice (orig-fun)
  (let ((result-orig (funcall orig-fun))
        (my-virtual-list (my-ivy-bookmark-name-filename-list)))
    (setq ivy--virtual-buffers (append ivy--virtual-buffers my-virtual-list))
    (append result-orig (mapcar #'car my-virtual-list))))

(advice-add 'ivy--virtual-buffers :around 'my-ivy-bookmark-append-advice)
;;(advice-remove 'ivy--virtual-buffers 'my-ivy-bookmark-append-advice)

ivy-switch-buffer が扱うのはあくまでバッファ名です。現実にあるバッファの名前を選んでそこへ切り替えます。なのでrecentfやブックマークのファイル名はバッファ名ではないので「仮想バッファ」の名前として扱うことで無理矢理処理しています。仮想バッファ名リストを生成する関数が ivy--virtual-buffers です。その関数は、 ivy--virtual-buffers という変数(関数と同じ名前でややこしいですが)に仮想バッファ名とファイル名の対応表を構築した上で仮想バッファ名のリストを返します。

なので ivy--virtual-buffers 関数にadviceをかけて仮想バッファを付け足すのが上のコードです。

ivy-switch-bufferで選べる候補が気に入らないという方はこのようなやり方で候補を追加してみてはいかがでしょうか。

ちなみに、 *scratch* が無くても常に *scratch* を選べるようにするには my-ivy-bookmark-append-advice が返すリストに一つ付け加えるだけです。

(defun my-ivy-bookmark-append-advice (orig-fun)
  (let ((result-orig (funcall orig-fun))
        (my-virtual-list (my-ivy-bookmark-name-filename-list)))
    (setq ivy--virtual-buffers (append ivy--virtual-buffers my-virtual-list))
    (append result-orig (mapcar #'car my-virtual-list) '("*scratch*"))))

こうしておくと、もし知らないうちに *scratch* を閉じてしまっていても *scratch* というバッファ名を選べて新しくバッファを作成することが出来ます。