abo-abo / swiper

Ivy - a generic completion frontend for Emacs, Swiper - isearch with an overview, and more. Oh, man!
https://oremacs.com/swiper/
2.3k stars 338 forks source link

Prefer virtual buffer over current buffer in ivy-switch-buffer #908

Open unhammer opened 7 years ago

unhammer commented 7 years ago

If I do

(package-initialize)
(require 'ivy)
(setq ivy-use-virtual-buffers t)
(require 'recentf)
(recentf-mode 1)

and open file2, then close it again, then open file1, then do ivy-switch-buffer and type file and enter, it'll stay in file1. I would expect file1 to be excluded from the list since it's the one I've already got open, and I would never want to switch to that. (It does the right thing if I don't actually close the buffer of file2 first, but not if it's showing because of ivy-use-virtual-buffers.)

abo-abo commented 7 years ago

ivy-switch-buffer uses the default switch-buffer sort order and puts the virtual buffers below the regular buffers.

If you want to do it any other way, you'll have to customize it in your config. See ivy-sort-functions-alist.

unhammer commented 7 years ago

So I could do something like this (probably possible to simplify that comparison fn):

  (add-to-list 'ivy-sort-functions-alist
               (cons #'internal-complete-buffer
                     (defun my-downsort-current-buffer (a b)
                       (if (and (equal a (buffer-name (current-buffer)))
                                (not (assoc a ivy--virtual-buffers)))
                           nil
                         (if (and (equal b (buffer-name (current-buffer)))
                                  (not (assoc b ivy--virtual-buffers)))
                             t
                           nil)))))

but it only works if I put :sort t into ivy-switch-buffer's call to ivy-read.

However, since I never want to switch to the current buffer, and since I have to modify ivy-switch-buffer anyway, I ended up with this instead:

(defun ivy-switch-buffer ()
  "Switch to another buffer."
  (interactive)
  (let ((this-command 'ivy-switch-buffer))
    (ivy-read "Switch to buffer: " 'internal-complete-buffer
              :matcher #'ivy--switch-buffer-matcher
              :preselect (buffer-name (other-buffer (current-buffer)))
              :action #'ivy--switch-buffer-action
              :keymap ivy-switch-buffer-map
              :predicate (defun my-not-current-buffer (b)
                           (not (equal (cdr b) (current-buffer)) ))
              :caller 'ivy-switch-buffer)))

– I don't suppose there's a way to add predicates to predefined ivy functions? Or could we perhaps have a defcustom for ivy-switch-buffer's :predicate (along with -other-window), e.g. defaulting to nil?

abo-abo commented 7 years ago

– I don't suppose there's a way to add predicates to predefined ivy functions? Or could we perhaps have a defcustom for ivy-switch-buffer's :predicate (along with -other-window), e.g. defaulting to nil?

I would like to add a general system for overriding the arguments to ivy-read, based on the :caller key. So you could write:

(ivy-set 'ivy-switch-buffer
         :predicate
         (lambda (b)
           (not (equal (cdr b) (current-buffer)))))

But I have to think about the interface a bit, to avoid rewriting it too much in the future.

Also maybe unify the new approach with the existing functions: ivy-set-actions ivy-set-sources ivy-set-display-transformer.

cubranic commented 7 years ago

I have ivy-use-virtual-buffers set to t (default Spacemacs config), and the ordering described by @unhammer happens even without closing file2. With file1 as the current buffer, and file2 as the previous (i.e., reachable with previous-buffer), the completions start with file2 selected, but as soon as I type the f prefix, it highlights file1.