@alvaro
sign in · lmno.lol

Emacs DWIM: swiper vs isearch vs phi-search

I've talked about DWIM in the past, that wonderful Emacs ability to do what ✨I✨ mean.

Emacs being hyper-configurable, we can always teach it more things, so it can do exactly what we mean.

There are no shortages of buffer searching packages for Emacs. I'm a fan of Oleh Krehel's swiper, but before that, I often relied on the built-in isearch. Swiper is my default goto mechanism and have it bound to C-s (replacing the built-in isearch-forward).

Swiper services most needs until I start combining with other tools. Take keyboard macros and multiple cursors. Both wonderful, but neither can rely on swiper to do their thing. Ok, swiper does, but in a different way.

Rather than binding C-s to swiper, let's write a DWIM function that's aware of macros and multiple cursors. It must switch between swiper, isearch, and phi-search depending on what I want (search buffer, define macro, or search multiple cursors).

Let's also tweak swiper's behavior a little further and prepopulate its search term with the active region. Oh, and I also would like swiper to wrap around (see ivy-wrap). But only swiper, not other ivy utilities. I know, I'm picky, but that's the whole point of DWIM… so here's my function to search forward that does exactly what ✨I✨ mean:

(defun ar/swiper-isearch-dwim ()
  (interactive)
  ;; Are we using multiple cursors?
  (cond ((and (boundp 'multiple-cursors-mode)
              multiple-cursors-mode
              (fboundp  'phi-search))
         (call-interactively 'phi-search))
        ;; Are we defining a macro?
        (defining-kbd-macro
          (call-interactively 'isearch-forward))
        ;; Fall back to swiper.
        (t
         ;; Wrap around swiper results.
         (let ((ivy-wrap t))
           ;; If region is active, prepopulate swiper's search term.
           (if (and transient-mark-mode mark-active (not (eq (mark) (point))))
               (let ((region (buffer-substring-no-properties (mark) (point))))
                 (deactivate-mark)
                 (swiper-isearch region))
             (swiper-isearch))))))

The above snippet searches forward, but I'm feeling a little off-balance. Let's write an equivalent to search backwards. We can then bind it to C-r, also overriding the built-in isearch-backward.

(defun ar/swiper-isearch-backward-dwim ()
  (interactive)
  ;; Are we using multiple cursors?
  (cond ((and (boundp 'multiple-cursors-mode)
              multiple-cursors-mode
              (fboundp  'phi-search-backward))
         (call-interactively 'phi-search-backward))
        ;; Are we defining a macro?
        (defining-kbd-macro
          (call-interactively 'isearch-backward))
        ;; Fall back to swiper.
        (t
         ;; Wrap around swiper results.
         (let ((ivy-wrap t))
           ;; If region is active, prepopulate swiper's search term.
           (if (and transient-mark-mode mark-active (not (eq (mark) (point))))
               (let ((region (buffer-substring-no-properties (mark) (point))))
                 (deactivate-mark)
                 (swiper-isearch-backward region))
             (swiper-isearch-backward))))))

These may be on the hacky side of things, but hey… they do the job. If there are better/supported ways of accomplishing a similar thing, I'd love to hear about it.