Skip to content

Additional Configuration

Omar Antolín Camarena edited this page Jan 1, 2021 · 76 revisions

This page lists examples of additional configuration that might be useful with selectrum.

Examples which show how to integrate selectrum with other packages only present the basic configuration needed to make use of them. You will need to look at the respective projects READMEs to checkout their other configuration options.

Configuration for built-ins

Minibuffer default add function

Completion commands can bind minibuffer-default-add-function to control which candidates a user gets offered when pressing M-n. A good default is to offer the symbol or file name that where a point before entering the minibuffer:

(autoload 'ffap-guesser "ffap")
(setq minibuffer-default-add-function
      (defun minibuffer-default-add-function+ ()
        (with-selected-window (minibuffer-selected-window)
          (delete-dups
           (delq nil
                 (list (thing-at-point 'symbol)
                       (thing-at-point 'list)
                       (ffap-guesser)
                       (thing-at-point-url-at-point)))))))

Complete file names at point

When completing file names in shell/comint selectrum gives you the same interface as you get for find-file. This means the completion does not exit after one path level like with other frameworks and you can conveniently navigate to the path you are interested in. If you want to be able to get this file completion mechanism globally you can use the following:

(autoload 'ffap-file-at-point "ffap")

(add-hook 'completion-at-point-functions
          (defun complete-path-at-point+ ()
            (let ((fn (ffap-file-at-point))
                  (fap (thing-at-point 'filename)))
              (when (and (or fn
                             (equal "/" fap))
                         (save-excursion
                           (search-backward fap (line-beginning-position) t)))
                (list (match-beginning 0)
                      (match-end 0)
                      #'completion-file-name-table)))) 'append)

With the above you can call the command completion-at-point to get file completion with the point after a path name. See also the option tab-always-indent.

Shadowed pathes with file-name-shadow-mode

The built-in file-name-shadow-mode works with selectrum out of the box. For example if you want to use it to hide shadowed pathes you can use the following:

(setq file-name-shadow-properties
      '(invisible t))

Configuration for external packages

Sorting for M-x with amx

(setq amx-backend 'selectrum)

Filtering with orderless

(setq selectrum-refine-candidates-function #'orderless-filter)
(setq selectrum-highlight-candidates-function #'orderless-highlight-matches)
;; If you also configure `completion-styles` for orderless you might want to use the 
;; following advice because orderless isn't well suited for initial gathering of 
;; candidates by completion in region.
(advice-add #'completion--category-override :filter-return
            (defun completion-in-region-style-setup+ (res)
              "Fallback to default styles for region completions with orderless."
              (or res
                  ;; Don't use orderless for initial candidate gathering.
                  (and completion-in-region-mode-predicate
                       (not (minibufferp))
                       (equal '(orderless) completion-styles)
                       '(basic partial-completion emacs22)))))

Minibuffer-actions with embark

You should bind embark commands like embark-act, embark-act-noexit and embark-export in minibuffer-local-map (as embark commands are not selectrum specific). For available commands and other embark configurations see the embark documentation and its wiki.

(add-hook 'embark-target-finders
	  (defun current-candidate+category ()
	    (cons (selectrum--get-meta 'category)
		  (selectrum-get-current-candidate))))

(add-hook 'embark-candidate-collectors
          (defun current-candidates+category ()
            (when selectrum-active-p
	      (cons (selectrum--get-meta 'category) 
		    (selectrum-get-current-candidates
		     ;; Pass relative file names for dired.
		     minibuffer-completing-file-name)))))

;; No unnecessary computation delay after injection.
(add-hook 'embark-setup-hook 'selectrum-set-selected-candidate)

(add-hook 'embark-input-getters
          (defun embark-selectrum-input-getter+ ()
            (when selectrum-active-p
              (let ((input (selectrum-get-current-input)))
                (if minibuffer-completing-file-name
                    ;; Only get the input used for matching.
                    (file-name-nondirectory input)
                  input)))))
                  
;; The following is not selectrum specific but included here for convenience.
;; If you don't want to use which-key as a key prompter skip the following code.

(setq embark-action-indicator
      (defun embark-which-key-setup+ ()
        (let ((help-char nil)
              (which-key-show-transient-maps t)
              (which-key-replacement-alist
               (cons '(("^[0-9-]\\|kp-[0-9]\\|kp-subtract\\|C-u$" . nil) . ignore)
                     which-key-replacement-alist)))
          (setq-local which-key-show-prefix nil)
          (setq-local which-key-persistent-popup t)
          (which-key--update)))
      embark-become-indicator embark-action-indicator)

(add-hook 'embark-pre-action-hook
          (defun embark-which-key-tear-down+ ()
            (kill-local-variable 'which-key-persistent-popup)
            (kill-local-variable 'which-key-show-prefix)
            (unless which-key-persistent-popup
              (which-key--hide-popup-ignore-command))))

Correcting spelling errors with flyspell-correct

The default interface (flyspell-correct-dummy), already works with Selectrum. To ensure that you are using the default interface, you can do

(setq flyspell-correct-interface #'flyspell-correct-dummy)

When flyspell-correct provides candidates to completing-read, it appends alternative actions ([SAVE], [ACCEPT (session)], [ACCEPT (buffer)], and [SKIP]) to the list of possible spelling corrections. As of 2020 August 28, flyspell-correct-dummy no longer allows these candidates to be sorted (meaning that the actions are now always at the end of the candidate list). If you are using an older version, you can achieve the same effect by advising flyspell-correct-dummy like below.

;; Not needed for newer versions of `flyspell-correct'.
(advice-add 'flyspell-correct-dummy :around
              (defun my--fsc-wrapper (func &rest args)
                (let ((selectrum-should-sort-p nil))
                  (apply func args))))

Working with projects in projectile

Since 2020-11-21 projectile auto-detects the active completion system. Therefore no additional configuration is necessary for selectrum.

Older versions of projectile used ido as default completion system. With such an older version, to use completing-read instead, do

(setq projectile-completion-system 'default)

Display minibuffer in a child frame with mini-frame

Make sure you have

(mini-frame-mode +1)

;; Make sure you don't override the display action:
; (setq selectrum-display-action nil)

to enable mini-frame support. With more recent Emacs versions (27.2 and above), selectrum will use a mini-frame automatically when you don't have a custom selectrum-display-action defined.

For older versions, you may also need the following fix, see #169:

;; workaround bug#44080, should be fixed in version 27.2 and above, see #169
(define-advice fit-frame-to-buffer (:around (f &rest args) dont-skip-ws-for-mini-frame)
  (cl-letf* ((orig (symbol-function #'window-text-pixel-size))
             ((symbol-function #'window-text-pixel-size)
              (lambda (win from to &rest args)
                (apply orig
                       (append (list win from 
                                     (if (and (window-minibuffer-p win)
                                              (frame-root-window-p win)
                                              (eq t to))
                                         nil
                                       to))
                               args)))))
    (apply f args)))

;; If you are using Gnome there are issues with dynamic resize set by `mini-frame-resize`:
;; https://github.com/muffinmad/emacs-mini-frame/commit/bac8ab2a1be8e1b2959f3d40ffa714cdf3c49a01
;; The following setting will fix that:
(setq x-gtk-resize-child-frames 'resize-mode) ; only for Gnome users

Display candidates in a posframe

A simple example to show the use of posframes with selectrum-display-action:

(setq selectrum-display-action '(display-buffer-show-in-posframe))

(defun display-buffer-show-in-posframe (buffer _alist)
  (frame-root-window
   (posframe-show buffer
                  :min-height 10
                  :min-width (frame-width)
                  :internal-border-width 1
                  :left-fringe 8
                  :right-fringe 8
                  :poshandler 'posframe-poshandler-frame-bottom-left-corner)))

(add-hook 'minibuffer-exit-hook 'posframe-delete-all)

Handle completion order for refs in magit with prescient

(define-advice magit-list-refs (:around (orig &optional namespaces format sortby)
                                        prescient-sort)
  "Apply prescient sorting when listing refs."
  (let ((res (funcall orig namespaces format sortby)))
    (if (or sortby
            magit-list-refs-sortby
            (not selectrum-should-sort-p))
            res
      (prescient-sort res))))

Handle complete-symbol with SLIME

SLIME is old enough that it doesn't obey the completion-in-region-function API, this doesn't matter with company (see slime-company) but normally it won't use selectrum for completions. To fix this, use the following advice:

(advice-add 'slime-display-or-scroll-completions :around
             (defun my--slime-completion-in-region (_ completions start end)
               (completion-in-region start end completions)))

Improve the completion of multi-occur.

multi-occur reads a list of buffers to search in. Unfortunately the default Emacs implementation does not interact well with selectrum, since it does not use completing-read-multiple. Instead it reads each buffer separately.

By overwriting the default multi-occur, this issue can be fixed:

(defun multi-occur (bufs regexp &optional nlines)
  (interactive (cons
                (mapcar #'get-buffer
                        (completing-read-multiple "Buffer: "
                                                  #'internal-complete-buffer))
                (occur-read-primary-args)))
  (occur-1 regexp nlines bufs))

Alternatively, if you are hesitant in overwriting Emacs functions, you can define your own function and use it instead of multi-occur.

(defun my-multi-occur (bufs regexp &optional nlines)
  (interactive (cons
                (mapcar #'get-buffer
                        (completing-read-multiple "Buffer: "
                                                  #'internal-complete-buffer))
                (occur-read-primary-args)))
  (multi-occur regexp nlines bufs))

See also https://github.com/raxod502/selectrum/issues/226

Clone this wiki locally