Skip to content
zhaojiangbin edited this page Mar 23, 2015 · 3 revisions
(require 'dash)
(require 's)
;; Return name of the first n buffers from (buffer-list).
(defun my/name-of-buffers (n)
  (let* ((bns (--remove
               (s-starts-with? " " it)
               (--map (buffer-name it) (buffer-list))))
         ;; Exclude the first element which is the current buffer.
         (k (- (length bns) 1)))
    (-take (if (> n k) k n) (-drop 1 bns))))

;; Given ("a", "b", "c"), return "1. a, 2. b, 3. c".
(defun my/number-names (list)
  (let ((nseq (--map (number-to-string it)
                     (number-sequence 1 (length list)))))
    (mapconcat 'identity
               (--zip-with (concat it ". " other) nseq list)
               ", ")))

;; Visit the (n - 1)th element in list. If other-window is not nil,
;; call switch-to-buffer-other-window.
(defun my/visit-buffer (list n &optional other-window)
  (let ((k (length list))
        (n (if (< n 1) 1 n))
        (f (if other-window 'switch-to-buffer-other-window 'switch-to-buffer)))
    (if (> n k) (error "No buffer at index %d" n))
    (funcall f (get-buffer (nth (- n 1) list)))))

(let ((list nil)
      (names nil))
  (global-set-key
   "\C-o"
   (defhydra my/switch-to-buffer
     (:exit t
      :body-pre (setq list (my/name-of-buffers 4)
                      names (my/number-names list)))
   "
_o_ther buffers: %s(identity names)

"
     ("o" (lambda (n) (interactive "p") (my/visit-buffer list n)) "this window")
     ("O" (lambda (n) (interactive "p") (my/visit-buffer list n t)) "other window") 
     ("<escape>" nil))))

Pressing C-o activates the hydra with names of the first 4 most recently visited "other" buffers listed:

  • A single o switches to the first buffer in that list, same as what M-:(switch-to-buffer (other-buffer)) would do. In other words, you can quickly switch between two buffers back and forth with C-o o.

  • Prefixed with a numeric prefix, say, 2 o, selects the 2nd buffer from the list, 3 o selects the 3rd, ... Negative prefixes and the numeric prefix 1 are same as no prefix - all select the first buffer.

  • O does the same as o except that the buffer is switched in the "other window" - i.e. by switch-to-buffer-other-window.

In practice, this hydra usually can have more heads to call, for example, ido-find-file, ido-switch-buffer, etc.

If you don't need to visit buffers in the "other window", a variant of this hydra can be:

(let ((list nil)
      (names nil))
  (global-set-key
   "\C-o"
   (defhydra my/switch-to-buffer
     (:exit t
      :body-pre (setq list (my/name-of-buffers 4)
                      names (my/number-names list)))
   "
other buffers: %s(identity names)

"
   ("o" (my/visit-buffer list 1))
   ("1" (my/visit-buffer list 1))
   ("2" (my/visit-buffer list 2))
   ("3" (my/visit-buffer list 3))
   ("4" (my/visit-buffer list 4))
   ("<escape>" nil))))

Then C-o 1 selects the 1st buffer, C-o 2 selects the 2nd, and so on, and still retain the quick C-o o.

Clone this wiki locally