15

Here's my problem: I use Emacs and get lots of buffers that are pretty useless all the time, like *Messages* or *Completions*.

I want to bind \C-y to close all buffers that start with * except for *shell* (and *shell* < k >) buffers.

To do that, I'd like to add some Emacs-Lisp in my .emacs file:

(defun string-prefix s1 s2
  (if (> (string-length s1) (string-length s2)) nil
    (string=? s1 (substring s2 0 (string-length s1))) ))

(defun curry2
  (lambda (f)
    (lambda (x)
      (lambda (y)
    (f x y) ))))

(defun filter
  (lambda (f l)
    (if (null? l) '()
      (let ((rest (cdr l)))
    (if (f (car l)) (cons (car l) rest)
      rest) ))))


(defun kill-useless (arg)
  (interactive "p")
  (map 'kill-buffer
       (filter
    (not ((curry2 string-prefix) "*shell*"))
    (list-buffers)
    ) ))

(global-set-key "\C-y" 'kill-useless)

I've already tested string-prefix and curry2 using Scheme and filter seems pretty straightforward. Sadly I just can't get kill-useless to work properly.

It says filter: Invalid function: (curry2 string-prefix).

Now, the thing is I kind of suck at Emacs-Lisp, I don't really use any Lisp except Scheme, and in Scheme (MIT), this works:

(filter ((curry2 string-prefix?) "*shell") '("*shell*" "*sh22" "eel"))
;Value 5: ("*shell*")

I'd like:

  1. a way to fix my code
  2. suggestions on how to do this in a different way

Thanks!

Dan Filimon
  • 1,419
  • 14
  • 16
  • 2
    The `*Message*` buffer is a useful one: every time Emacs tell you something, it let it there, so you can for example copy and paste it to google to find a solution on google. Beware also that when you will probably use some mode (like magit) who will use such buffer in a useful way. – Rémi Feb 24 '11 at 09:59
  • Yes, I ended up keeping that buffer too. Thanks for the tip! – Dan Filimon Feb 24 '11 at 18:30

6 Answers6

30

C-h f kill-matching-buffers RET

kill-matching-buffers is an interactive compiled Lisp function in `files.el'.

(kill-matching-buffers REGEXP &optional INTERNAL-TOO)

Kill buffers whose name matches the specified REGEXP. The optional second argument indicates whether to kill internal buffers too.

phils
  • 71,335
  • 11
  • 153
  • 198
  • 2
    +1 for built-in function although an actual example of how to use it would have been far more helpful. – Dan Filimon Feb 24 '11 at 18:29
  • 4
    It keeps asking if I want to close every buffer though (even if unmodified) - is there a way to avoid that ? – Zitrax Jan 28 '14 at 12:07
  • Yeah, the UI of that function is really bad. In effect one starts pressing `y` really quick and when a **modified** buffer shows up you've killed it before noticing the question is slightly different... – olejorgenb Feb 19 '17 at 12:22
  • 1
    @Zitrax If you do C-h f on kill-matching-buffers, it gives more details. You can avoid the prompt for closing every buffer with something like (kill-matching-bufers REGEXP nil t) (its the t that does this - nil is for setting internal-too to nil (you can change this if required - check the docs on what each optional does) – Ashok Khanna Sep 17 '21 at 08:06
13

An alternate approach:

(require 'cl)

(defun is-useless-buffer (buffer)
  (let ((name (buffer-name buffer)))
    (and (= ?* (aref name 0))
         (not (string-match "^\\*shell\\*" name)))))

(defun kill-useless-buffers ()
  (interactive)
  (loop for buffer being the buffers
        do (and (is-useless-buffer buffer) (kill-buffer buffer))))
Sean
  • 29,130
  • 4
  • 80
  • 105
  • I like this approach the most. I've also added in a check to keep the \*Messages* buffer. Congrats Sean! You win :) Also, what does `(require 'cl)` do exactly? – Dan Filimon Feb 24 '11 at 18:28
  • 1
    It brings in Emacs's Common Lisp package, which is where the (extremely handy) "loop" macro lives. The whole package is very useful; the first line in my .emacs file is (require 'cl). See http://www.gnu.org/software/emacs/manual/html_mono/cl.html – Sean Feb 24 '11 at 19:21
  • @EmmanuelGoldstein Then you can add some logging to see if the buffers you think are useless really are, according to `is-useless-buffer`. For example, you could change the `do (and ...)` part to `do (message "Buffer %s %s useless" (buffer-name buffer) (if (is-useless-buffer buffer) "is" "is not"))`. – Sean Aug 11 '20 at 16:58
  • Thanks for the prompt response, @Sean. I am no elisp programmer. For me all useless buffers would be basically all except internal Emacs buffers (starting with *). How to implement this? – Emmanuel Goldstein Aug 12 '20 at 07:29
  • 1
    @EmmanuelGoldstein If it wasn't clear, after executing the above code (for example by having it in your startup file when you run Emacs), you still need to run the `kill-useless-buffers` command by typing `M-x` (hold down meta/alt and press `x`), `kill-useless-buffers`, and the Return key. Also, there will be no visible indication that the buffers were killed, unless you happen to be looking at one when you run the command. I just started Emacs, created some buffers named `*foo*`, `*bar*`, and `*baz*`, then ran that command, and those buffers were deleted. – Sean Aug 12 '20 at 17:21
  • 1
    To change this code to delete everything except buffers whose names start with an asterisk, just change the body of the `is-useless-buffer` function (everything from the opening parenthesis on the second line to the next-to-last closing parenthesis on the last line) to `(string-match-p (rx string-start (not (any "*"))) (buffer-name buffer))`. Then you'll need to evaluate the revised definition to make it take effect, most easily by pressing control-meta-X while the cursor is inside the function. – Sean Aug 12 '20 at 17:31
  • Thank you, very much appreciated. – Emmanuel Goldstein Aug 13 '20 at 10:10
5

Keep in mind elisp isn't scheme, or even common lisp. The syntax and semantics are different. For example, defun requires a parameters list surrounded by parentheses. Also, currying isn't really possible in elisp.

Fortunately, elisp has builtins for most of what you want to do. For string-prefix you can use the string-prefix-p builtin. For filter, you can use remove-if-not, or remove-if for the inverse.

For the currying you can use the apply-partially builtin function. To get a function that matches strings with the prefix "*shell*", try something like this:

(apply-partially 'string-prefix-p "*shell*")

You can use it like this:

(mapcar
 (apply-partially 'string-prefix-p "*shell*")
 '("*shell*" "foo" "*shell*<2>"))

; results in
(t nil t)

(require 'cl) ; for remove-if
(remove-if
 (apply-partially 'string-prefix-p "*shell*")
 '("*shell*" "foo" "*shell*<2>"))

; results in
("foo")
ataylor
  • 64,891
  • 24
  • 161
  • 189
  • Thanks for the heads up, but I gave the _answer_ to the person who gave me a working solution. Trying to fiddle around to get this work would not have been too much fun! – Dan Filimon Feb 24 '11 at 18:32
0

I found kill-matching-functions prompted me for unmodified buffers, which isn't what I wanted. The function below will kill buffers matching a prefix (as in the title of the question). Not exactly what you wanted, but maybe people like me who come here from Google will find this useful.

(require 'cl)
(defun kill-buffers-with-prefix (prefix)
  "Kill buffers whose names start with the given prefix"
  (interactive "sPrefix to kill: ")
  (loop for buffer in (buffer-list)
        do (if (string-prefix-p prefix (buffer-name buffer))
               (kill-buffer buffer))))
Jay Conrod
  • 28,943
  • 19
  • 98
  • 110
0

To kill all other buffers

(defun px-kill-other-buffers ()
  "Kill all other buffers."
  (interactive)
  (mapc 'kill-buffer (delq (current-buffer) (buffer-list))))

To search the beginning of a string

(defun string-starts-with-p (string prefix)
    "Return t if STRING starts with prefix."
    (and
     (string-match (rx-to-string `(: bos ,prefix) t) string)
     t))
yPhil
  • 8,049
  • 4
  • 57
  • 83
0

It can be a good idea to see what you will delete before deleting it, to play safe.

In Icicles, by default C-x k is a multi-command that you can use to kill any number of buffers that match your minibuffer input. In this case, you would type * TAB to see all buffer names starting with * as completion candidates.

You can then narrow the matches, in several ways. When all of the matches remaining are what you want, hit C-! to delete all of those buffers.

In the case you presented, you do not want to delete buffers named *shell.... So after * TAB you would hit S-SPC and then enter another pattern to match: shell, then S-TAB. That narrows to only the *shell... buffers that you do not want to kill. You then hit C-~ to subtract those matches (complement). That leaves all of the buffers except the shell buffers. Hit C-! and they're all killed.

You can also kill individual buffers by just control-clicking their names in *Completions*: C-mouse-2.

More generally, in Icicles every multi-command that reads a buffer name lets you use S-delete (Shift + the Delete key) to kill buffer candidates.

http://www.emacswiki.org/emacs/Icicles_-_Multi-Commands

http://www.emacswiki.org/emacs/Icicles_-_More_About_Multi-Commands

Drew
  • 29,895
  • 7
  • 74
  • 104