5

There are three Ivy completion styles:

ivy--regex-plus
ivy--regex-ignore-order
ivy--regex-fuzzy

C-o (hydra-ivy/body) m toggles between ivy "ivy--regex-plus" and "ivy--regex-fuzzy".

How to switch to ivy--regex-ignore-order completion style?

For example, if I wanted a list of all functions that contain the string "spell", from help describe functions I would: enter "spell" and switch to ivy--regex-ignore-order.

I am using Emacs 25.2.1 with Evil Spacemacs and Ivy on Linux.

6
  • The link you provide lists 5 possible regexp builder functions. What is your desired behaviour? Providing more toggle keys, for switching between more possible builders, or changing m (ivy-toggle-fuzzy) to rotate between them all?
    – Basil
    Commented Jul 3, 2017 at 5:50
  • Please, changing m (matcher) to rotate between three completion styles: ivy--regex-plus, ivy--regex-ignore-order, ivy-toggle-fuzzy
    – wolfv
    Commented Jul 3, 2017 at 6:58
  • For your specific use case of finding functions containing "spell" I don't think you need to change the matching style at all? Just do C-h f and search for either spell or ^ spell -- that is, delete the leading ^ or add a space after it. That said, I think your suggestion to be able to toggle matching styles is a good one and perhaps should be a feature request for ivy.
    – glucas
    Commented Jul 3, 2017 at 12:44
  • 1
    I added this as a feature request: github.com/abo-abo/swiper/issues/1093,
    – glucas
    Commented Jul 3, 2017 at 13:37
  • @Basil Looks like abo-abo likes the suggestion (link above). Would you like to create a patch request to ivy for your changes? Otherwise I can submit one. I'm thinking the builders list could just have a cons cell with the display name to use in the dynamic hydra docstring, so ivy--matcher-desc could look it up.
    – glucas
    Commented Jul 3, 2017 at 16:19

1 Answer 1

5

The code in this answer is now part of Ivy: https://github.com/abo-abo/swiper/commit/ae438ff62fa3d9b98d899afc0e97c13be2148725

Within ivy-hydra, the m key is bound to the command ivy-toggle-fuzzy, which is hard-coded to toggle the variable ivy--regex-function between the regexp-building functions ivy--regex-fuzzy and ivy--regex-plus.

The ivy-hydra panel is similarly limited by the function ivy--matcher-desc to describing ivy--regex-fuzzy as "fuzzy" and all other values as "ivy".

I am not familiar with hydra and whether it facilitates hot-swapping key bindings and commands, so my only suggestion (other than possibly submitting a feature request to the ivy project) is to override the aforementioned functions to exhibit the desired behaviour:

(defvar my-ivy-builders '(ivy--regex-plus
                          ivy--regex-ignore-order
                          ivy--regex-fuzzy)
  "Preferred values for `ivy--regex-function'.")

(define-advice ivy--matcher-desc (:override () my-expand-matchers)
  "Return a brief description of `ivy--regex-function'."
  (pcase ivy--regex-function
    ('ivy--regex-fuzzy        "fuzzy")
    ('ivy--regex-ignore-order "order")
    ('ivy--regex-plus         "plus")
    (_                        "other")))

(define-advice ivy-toggle-fuzzy (:override () my-rotate-builders)
  "Slide `ivy--regex-function' across `my-ivy-builders'."
  (when my-ivy-builders
    (setq ivy--old-re nil)
    (setq ivy--regex-function
          (or (cadr (memq ivy--regex-function my-ivy-builders))
              (car my-ivy-builders)))))

Here, my-ivy-builders holds all preferred regexp building functions which are suitable as values of the variable ivy--regex-function. The ivy--matcher-desc advice simply adds support for describing more such functions with a human-readable name. You can name them whatever you like, but make sure they fit within 5 columns, otherwise the ivy-hydra panel's formatting will become distorted. Finally, the ivy-toggle-fuzzy advice slides ivy--regex-function across its permitted values as listed in my-ivy-builders. You can change the initial order of my-ivy-builders to match both your taste and your default value for ivy--regex-function.

I use :override advice instead of redefining the functions with defun because the former gets reported by the help system (i.e. describe-function). Note that define-advice was only added in Emacs 25.1; under Emacs 24 the advice should be written more like

(defvar my-ivy-builders '(ivy--regex-plus
                          ivy--regex-ignore-order
                          ivy--regex-fuzzy)
  "Preferred values for `ivy--regex-function'.")

(defun my-ivy-matcher-descs ()
  "Return a brief description of `ivy--regex-function'."
  (pcase ivy--regex-function
    ('ivy--regex-fuzzy        "fuzzy")
    ('ivy--regex-ignore-order "order")
    ('ivy--regex-plus         "plus")
    (_                        "other")))

(advice-add 'ivy--matcher-desc :override #'my-ivy-matcher-descs)

(defun my-ivy-rotate-builders ()
  "Slide `ivy--regex-function' across `my-ivy-builders'."
  (when my-ivy-builders
    (setq ivy--old-re nil)
    (setq ivy--regex-function
          (or (cadr (memq ivy--regex-function my-ivy-builders))
              (car my-ivy-builders)))))

(advice-add 'ivy-toggle-fuzzy :override #'my-rotate-builders)

I think plain (') quotes were also not always supported by pcase, so you may need to change them to backquotes (`).

Update

Further to the helpful comments by glucas below, you can forgo advising the function ivy-toggle-fuzzy and instead replace it with an adjusted my-ivy-rotate-builders in the hydra keymap under the default keybinding of m:

(defvar my-ivy-builders '(ivy--regex-plus
                          ivy--regex-ignore-order
                          ivy--regex-fuzzy)
  "Preferred values for `ivy--regex-function'.")

(define-advice ivy--matcher-desc (:override () my-expand-matchers)
  "Return a brief description of `ivy--regex-function'."
  (pcase ivy--regex-function
    ('ivy--regex-fuzzy        "fuzzy")
    ('ivy--regex-ignore-order "order")
    ('ivy--regex-plus         "plus")
    (_                        "other")))

(defun my-ivy-rotate-builders ()
  "Slide `ivy--regex-function' across `my-ivy-builders'."
  (interactive)
  (when my-ivy-builders
    (setq ivy--old-re nil)
    (setq ivy--regex-function
          (or (cadr (memq ivy--regex-function my-ivy-builders))
              (car my-ivy-builders)))))

(with-eval-after-load 'ivy-hydra
  (define-key hydra-ivy/keymap "m" #'my-ivy-rotate-builders))

Perhaps someone more familiar with hydra can also suggest an easy way to modify the hydra-ivy hint/panel in-place instead of advising ivy--matcher-desc.

9
  • Wow Basil, that's impressive. You have inspired me to learn elisp. Unfortunately the ivy--regex-ignore-order option is not working. I tested this on help describe functions ispell: "order" appears in the matcher rotation, but "ispell" function does not appear in the list (the "order" list is identical to the "plus" list).
    – wolfv
    Commented Jul 3, 2017 at 13:00
  • 1
    I've just tested this and it works great for me. Note that you can just rebind "m" in the hydra-ivy/keymap rather than using advice, so I've made my-ivy-rotate-builders a function of its own.
    – glucas
    Commented Jul 3, 2017 at 13:10
  • 1
    @user2867994 Can you explain your test case a bit more? It sounds like you are searching for a single string, in which case order is not relevant. Also note that if you're matching a regex that starts with ^ (which ivy does insert by default...) then that will only match the beginning of the string even when using ivy--regex-ignore-order. Try this instead: C-h f buffer spell (note no ^ char) which will match nothing, then rotate the matchers and see that it now matches e.g. ispell-buffer.
    – glucas
    Commented Jul 3, 2017 at 13:14
  • @glucas So that's what the '^' is for! After removing the '^', Basil's functions work great. Sorry, my test case from 3 comments up has a typo. The test case should say: from help describe functions "spell", "order" appears in the matcher rotation, but "ispell" function does not appear in the list.
    – wolfv
    Commented Jul 3, 2017 at 13:27
  • 1
    Ivy is using a regular expression to find the matches, and ^foo would mean "match strings starting with foo". Usually when I'm searching for a substring I just hit space: with ^ foo ivy will find matches with foo anywhere, rather than at the beginning. Order becomes relevant when you have multiple substrings and don't know which order they might appear in, so foo bar could match "bar-foo" or "foo-bar". You can use other regular expression characters with ivy, e.g. you could use foo$ to only match things that end with "foo".
    – glucas
    Commented Jul 3, 2017 at 13:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.