11

I'd like to make a simple change to Emacs so that the next-buffer and previous-buffer commands (which I have bound to C-x <RIGHT> and C-x <LEFT> will skip over the *Messages* buffer.

I'm using Emacs 24 and the Emacs Starter Kit.

I've read the following related questions and answers, but they are not what I want:

Here are some of the reasons why they don't work:

  • I'd like to keep it as simple as possible. Fewer configuration changes are better.
  • I don't want to kill or prevent *Messages* altogether.
  • (add-to-list 'ido-ignore-buffers "^\*Messages\*" helps with my C-x b (ido-switch-buffer) but does not change how next-buffer and previous-buffer behave.
Community
  • 1
  • 1
David J.
  • 31,569
  • 22
  • 122
  • 174
  • I think you should have a look on IDO. It's not an answer, but recommendation, because (C-x )xN is far less convenient than C-x b xN. – desudesudesu Jan 16 '13 at 04:46
  • I suggest readers refer instead to http://emacs.stackexchange.com/q/17687/454 (which is more recent). – phils Oct 27 '15 at 22:28

4 Answers4

16

This way you can avoid the infinite loop:

(defun next-code-buffer ()
  (interactive)
  (let (( bread-crumb (buffer-name) ))
    (next-buffer)
    (while
        (and
         (string-match-p "^\*" (buffer-name))
         (not ( equal bread-crumb (buffer-name) )) )
      (next-buffer))))
(global-set-key [remap next-buffer] 'next-code-buffer)

This code loops over non-starred buffers ("^\*"). For your case (only avoid *Messages*) it would be:

(defun next-code-buffer ()
  (interactive)
  (let (( bread-crumb (buffer-name) ))
    (next-buffer)
    (while
        (and
         (equal "*Messages*" (buffer-name))
         (not ( equal bread-crumb (buffer-name) )) )
      (next-buffer))))
(global-set-key [remap next-buffer] 'next-code-buffer)

You can write previous-code-buffer just replacing every next-buffer with previous-buffer.

RubenCaro
  • 1,419
  • 14
  • 12
7

The simplest I can think of is defining an advice for both functions. Here it is for next-buffer. Similarly would be for previous-buffer. You can also define a configuration variable to enable/disable the behavior (or activating/deactivating the advice):

(defadvice next-buffer (after avoid-messages-buffer-in-next-buffer)
  "Advice around `next-buffer' to avoid going into the *Messages* buffer."
  (when (string= "*Messages*" (buffer-name))
    (next-buffer)))

;; activate the advice
(ad-activate 'next-buffer)

Maybe you can compare buffers in some other way instead of its string name, but that will work. The code for previous buffer is almost the same. I don't know either if there is a way of calling the original function without triggering the advice once inside the advice itself, but again, the code will work even if the name of the buffer is tested afterwards (will fail if you just have one buffer, and it is the messages buffer; some code can check if there is just one buffer and don't call next-buffer again).

If you want to use a standalone function that does the same thing:

(defun my-next-buffer ()
  "next-buffer, only skip *Messages*"
  (interactive)
  (next-buffer)
  (when (string= "*Messages*" (buffer-name))
      (next-buffer)))

(global-set-key [remap next-buffer] 'my-next-buffer)
(global-set-key [remap previous-buffer] 'my-next-buffer)
event_jr
  • 17,467
  • 4
  • 47
  • 62
Diego Sevilla
  • 28,636
  • 4
  • 59
  • 87
  • 5
    I would avoid using `defadvice` for functions where you have no control over who would call them. In this case, I would define new functions and bind them to the keys in question, that way you would be sure there will be no unwanted side-effects. – Lindydancer Jan 14 '13 at 18:35
  • Mmm... maybe you're right, yes, but the OP asked specifically for changing how these functions works. Also this is what `defadvice` is for, right? Anyway, yes, redefining the key bindings is another viable option, of course. – Diego Sevilla Jan 14 '13 at 18:55
  • I'd be happy to adjust my keybindings to point to new functions. – David J. Jan 14 '13 at 19:56
  • @DavidJames Answer updated with new function and bindings. Repeat & replace with `previous/left` for the complementary versions. – Trey Jackson Jan 14 '13 at 23:27
  • 1
    @DiegoSevilla `defadvice` is not for this, abusive examples of it from the wiki not withstanding. `defadvice` is a dangerously sharp knife that should be reserved for specialy jobs. `[remap ]` binding was designed to solve this. – event_jr Jan 16 '13 at 15:22
6

This is what I'm using, based on Diego's answer:

(setq skippable-buffers '("*Messages*" "*scratch*" "*Help*"))

(defun my-next-buffer ()
  "next-buffer that skips certain buffers"
  (interactive)
  (next-buffer)
  (while (member (buffer-name) skippable-buffers)
    (next-buffer)))

(defun my-previous-buffer ()
  "previous-buffer that skips certain buffers"
  (interactive)
  (previous-buffer)
  (while (member (buffer-name) skippable-buffers)
    (previous-buffer)))

(global-set-key [remap next-buffer] 'my-next-buffer)
(global-set-key [remap previous-buffer] 'my-previous-buffer)

It is not great yet, because it will hang if there are no buffers other than the skippable-buffers I list. I use C-g to break out of the loop when it happens as a hackaround.

David J.
  • 31,569
  • 22
  • 122
  • 174
2

As RubenCaro's answer points out, the other answers can enter infinite loops. I thought David James' approach of a skippable buffers list was a bit nicer, though, so here's a variant of that.

(setq my-skippable-buffers '("*Messages*" "*scratch*" "*Help*"))

(defun my-change-buffer (change-buffer)
  "Call CHANGE-BUFFER until current buffer is not in `my-skippable-buffers'."
  (let ((initial (current-buffer)))
    (funcall change-buffer)
    (let ((first-change (current-buffer)))
      (catch 'loop
        (while (member (buffer-name) my-skippable-buffers)
          (funcall change-buffer)
          (when (eq (current-buffer) first-change)
            (switch-to-buffer initial)
            (throw 'loop t)))))))

(defun my-next-buffer ()
  "`next-buffer' that skips `my-skippable-buffers'."
  (interactive)
  (my-change-buffer 'next-buffer))

(defun my-previous-buffer ()
  "`previous-buffer' that skips `my-skippable-buffers'."
  (interactive)
  (my-change-buffer 'previous-buffer))

(global-set-key [remap next-buffer] 'my-next-buffer)
(global-set-key [remap previous-buffer] 'my-previous-buffer)
phils
  • 71,335
  • 11
  • 153
  • 198
  • Reproduced in a slightly improved form at http://emacs.stackexchange.com/a/17694/454 -- use that version instead of this (and in general you are probably better to use that *question* instead of this one, as several of the answers here, including the accepted answer, are quite flawed). – phils Oct 27 '15 at 22:25