Álvaro Ramírez
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.