3

I'm using Emacs 26. There are many Elisp commands which offer the "next" buffer as part of the command's execution or after the command completes. I am looking for a way to keep buffers whose names start with * from showing up as the "next" buffer in all such commands.

Some commands which would have to change are kill-buffer, switch-to-buffer, etc.

I could search for all Elisp commands which offer the "next" buffer and write my own versions of these commands. However, I'm wondering if there might be more general way to keep all buffers whose names start with * from being treated as the "next" buffer in any command.

5
  • emacs.stackexchange.com/tags/elisp/info
    – Drew
    Commented Dec 7, 2021 at 19:12
  • Your notion of "next" buffer is not clear. It doesn't sound like you mean the buffer switched to by command next-buffer, for example. And you say, "in many commands". Please clarify what you mean.
    – Drew
    Commented Dec 7, 2021 at 19:15
  • You can advise individual functions that use buffers, filtering, for example, what function buffer-list returns.
    – Drew
    Commented Dec 7, 2021 at 19:17
  • There are a number of functions which utilize buffer-list, and this is what I mean by "many commands". I am trying to avoid advising each and every function which utilizes buffer-list. And it doesn't seem like buffer-list itself can be advised. This is the reason for my asking my question here.
    – HippoMan
    Commented Dec 7, 2021 at 19:50
  • As an example of the complexities of this task, consider kill-buffer. After killing, it tries to present the value of (other-buffer). However, that function is defined in the emacs source file, buffer.c, and it doesn't seem to be overridable via advice.
    – HippoMan
    Commented Dec 7, 2021 at 20:44

2 Answers 2

2

There's no need to advise functions. M-x info-apropos buffer-list gives us a link to this info node: The Buffer List.

This paragraph in particular offers a solution:

To change the order or value of a specific frame’s buffer list, set that frame’s ‘buffer-list’ parameter with ‘modify-frame-parameters’.

There isn't a with-frame-parameters as far as I know, but we can monkey-wrench one pretty easily:

(defmacro clean-buffer-list (&rest body)
  "Remove all entries from the current frame's buffer-list parameter
that match the regex, \" ?\\\\\\*\"; update the parameter, and then 
execute BODY in a `progn'. Note that this permanently modifies the 
frame's buffer-list as a side effect."
  (declare (indent defun)
           (debug (body)))
  `(let ((buffers ',(cl-loop for x in (frame-parameter nil 'buffer-list)
                           unless (string-match (rx (: (? " ") "*")) (buffer-name x))
                           collect x into rs
                           finally return rs)))
     (set-frame-parameter nil 'buffer-list buffers)
     (progn ,@body)))

I tested it like this:

M-x buffer-menu <RET>
(other-buffer) ; returns "*Buffer List*"
(clean-buffer-list (other-buffer)) ; returns "a.el"

The *-buffers still live on in the fundamental buffer-list, but I'm not sure how to access them, and I think that's beyond the scope of this question. The linked info page should give you some pointers if that's what you need. The macro right now might also cause some wonky behaviour if you're calling it from within a *-buffer; I haven't tested that extensively.

1
  • Thank you very much! Yours is a variation on what I came up with (see my Answer).
    – HippoMan
    Commented Dec 9, 2021 at 0:12
1

I figured it out:

(defun my-buffer-predicate (buffer)
  (let ((buffname (buffer-name buffer)))
    (not (or (string-prefix-p "*" buffname) 
             (string-prefix-p " *" buffname)))
  ))

(defun my-set-buffer-predicate ()
  (modify-all-frames-parameters
     (list
      (cons 'buffer-predicate #'my-buffer-predicate))))

(add-hook 'after-make-frame-functions 'my-set-buffer-predicate)

And I also run (my-set-buffer-predicate) at emacs startup.

This excludes buffers whose names start with "*" and " *" from many commands which automatically switch to their idea of a "next" or "previous" or "other" buffer. So, it's thankfully not necessary for me to try to advise a myriad of existing functions. Also, this doesn't inhibit a switch to *scratch* if there are no existing buffers to switch to, because that logic is handled elsewhere in emacs. The *scratch* buffer shows up as a fallback when no other eligible buffers exist.

All of this is exactly the functionality that I have been looking for.

1
  • 1
    Very nice - thanks!
    – NickD
    Commented Dec 9, 2021 at 18:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.