selectrum/consult/marginaliaの設定

ivy/counsel系の代替として、selectrumやconsultが最近来ている。

特徴をまとめた方がいる:

  • selectrum … ivyに対応。ivyよりも実装はシンプル。
  • consult ... counselに対応。counselにはないユニーク機能を持つコマンドを提供したり。consult-bufferはその例。
  • marginalia ... ivy-richに対応。

最近使っているので設定を残す。
まずはseletrumとmarginalia-mode。

(eval-when-compile (require 'selectrum))
(eval-when-compile (require 'selectrum-prescient))
(declare-function selectrum-mode "selectrum")
(declare-function selectrum-prescient-mode "selectrum-prescient")

(require 'selectrum)
(selectrum-mode +1)
(define-key selectrum-minibuffer-map (kbd "C-l") #'selectrum-backward-kill-sexp)
(setq selectrum-max-window-height 20)

;; to make sorting and filtering more intelligent
(require 'selectrum-prescient)
(selectrum-prescient-mode +1)
(require 'prescient)
(prescient-persist-mode 1)
(setq prescient-aggressive-file-save t)
;; selectrum-prescient-enable-filtering : t
;; selectrum-prescient-enable-sorting : t

;; selectrumのivy-rich
;; https://github.com/minad/marginalia
(require 'marginalia)
(marginalia-mode +1)
(setq marginalia-align-offset 25) ; 右端にどれだけスペースを詰めるか
;; (setq marginalia-truncate-width 80)
;; (setq marginalia-margin-threshold 200)
;; (setq marginalia-separator-threshold 120)

;; marginalia-annotators 初期値およびサイクル順を指定する
(setq marginalia-annotators
      '(marginalia-annotators-light marginalia-annotators-heavy nil))

;; marginalia-annotatorsをサイクルする
(define-key minibuffer-local-map (kbd "C-M-a") #'marginalia-cycle)

;; When using Selectrum, ensure that Selectrum is refreshed
;; when cycling annotations.
(advice-add #'marginalia-cycle :after
            (lambda () (when (bound-and-true-p selectrum-mode)
                         (selectrum-exhibit 'keep-selected))))

次はconsult。

(require 'consult)

;; consult-line ... swiper の代替
;; consult-isearch ... isearch中にconsultインタフェースでクエリを再入力し、isearch再実行
;; consult-multi-occur ... multi-occurの代替
;; consult-focus-line ... クエリにヒットする部分のみを抽出して「表示」する(他が隠れる, narrowing)。
;; その後、C-uつきで呼び出すと、隠れていた部分が表示される(もとに戻る, widen)
;; consult-recent-file ... 最近開いたファイルを選択

(setq consult-async-refresh-delay 0.2)

(global-set-key (kbd "C-z C-n") 'consult-recent-file)

;; consult-buffer時などにおけるpreview機能の調整
(setq consult-preview-key nil) ; 常にプレビューOFF
;; (setq consult-preview-key 'any) ; 常にプレビューON
;; (setq consult-preview-key (kbd "C-M-p")) ; これを押したときだけプレビュー

;; 以下のコマンド群については、M-.でプレビュー
(dolist (cmd '(consult-ripgrep consult-grep consult-bookmark consult-recent-file))
    (setf (alist-get cmd consult-config) `(:preview-key ,(kbd "M-."))))

;; カーソル下のシンボルを拾ってconsult-line発動
(defun consult-line-symbol-at-point ()
  (interactive)
  (consult-line (thing-at-point 'symbol)))

(defun my-isearch-or-consult (use-consult)
  (interactive "p")
  (cond ((eq use-consult 1)
         (call-interactively 'isearch-forward))
        ((eq use-consult 4)
         (call-interactively 'consult-line-symbol-at-point))
        ((eq use-consult 16)
         (call-interactively 'consult-line))))

(global-set-key (kbd "C-s") #'my-isearch-or-consult)
(define-key isearch-mode-map (kbd "C-:") #'consult-isearch) ; 実質的な絞り込み
(define-key isearch-mode-map (kbd "C-;") #'consult-line) ; swiperに対応

;; 絞り込み対象をバッファに限定 : b SPC
;; 絞り込み対象をファイル(recentf)に限定 : f SPC
;; 絞り込み対象をブックマークに限定 : m SPC
(global-set-key (kbd "C-x b") #'consult-buffer)

;; consult--source-fileの公式実装では「現在バッファとして保持しているファイル」が
;; ファイル(recentf)の検索対象から除外されるので、
;; それをやめるため単純にitemsにrecentf-listだけを持ってくる。
;; この設定の影響範囲はconsult-bufferのみ。consult-recent-fileは影響を受けない。
(setq consult--source-file
  `(:name     "File"
    :narrow   ?f
    :category file
    :face     consult-file
    :history  file-name-history
    :action   ,#'consult--file-action
    :enabled   ,(lambda () recentf-mode)
    :items ,recentf-list))

;; consult-ripgrepがデフォルトで複数ファイルを対象にした串刺し検索なので、
;; 検索対象をカレントバッファに限定
(defun consult-ripgrep-current-buffer ()
  "Call `consult-line' for the current buffer (a single file)."
  (interactive)
  (let ((consult-ripgrep-command
         (concat "rg "
                 "--null "
                 "--line-buffered "
                 "--color=ansi "
                 "--max-columns=250 "
                 "--no-heading "
                 "--line-number "
                 ;; adding these to default
                 "--smart-case "
                 "--hidden "
                 "--max-columns-preview "
                 ;; add back filename to get parsing to work
                 "--with-filename "
                 ;; defaults
                 "-e ARG OPTS "
                 (shell-quote-argument buffer-file-name))))
    (consult-ripgrep)))