Subject: 25.0.50; New deletion commands in buffer-menu [PATCH]

From: Filipp Gunbin <[email-masked]>
Date: Wed, 29 Apr 2015 20:55:42 +0300

What about these new commands in buffer-menu?  I find these useful in
sessions with lots of buffers.

`D m' Prompt for major mode name and delete all buffers in that mode.

`D b' Prompt for regexp and mark for deletion all buffers with matching
names.

`D f' Prompt for regexp and mark for deletion all buffers with mathing
file names.

Filipp

diff --git a/lisp/buff-menu.el b/lisp/buff-menu.el
index 91bc038..18264f2 100644
--- a/lisp/buff-menu.el
+++ b/lisp/buff-menu.el
@@ -96,6 +96,9 @@ This is set by the prefix argument to `buffer-menu' and related
 commands.")
 (make-variable-buffer-local 'Buffer-menu-files-only)
 
+(defvar Buffer-menu--regexp-history nil
+  "History list for buffer name regular expressions.")
+
 (defvar Info-current-file) ; from info.el
 (defvar Info-current-node) ; from info.el
 
@@ -130,6 +133,9 @@ commands.")
     (define-key map (kbd "M-s a C-s")   'Buffer-menu-isearch-buffers)
     (define-key map (kbd "M-s a M-C-s") 'Buffer-menu-isearch-buffers-regexp)
     (define-key map (kbd "M-s a C-o") 'Buffer-menu-multi-occur)
+    (define-key map (kbd "D m") 'Buffer-menu-delete-by-major-mode)
+    (define-key map (kbd "D b") 'Buffer-menu-delete-by-buffer-name-regexp)
+    (define-key map (kbd "D f") 'Buffer-menu-delete-by-file-name-regexp)
 
     (define-key map [mouse-2] 'Buffer-menu-mouse-select)
     (define-key map [follow-link] 'mouse-face)
@@ -243,7 +249,13 @@ In Buffer Menu mode, the following commands are defined:
 \\[Buffer-menu-toggle-read-only]    Toggle read-only status of buffer on this line.
 \\[revert-buffer]    Update the list of buffers.
 \\[Buffer-menu-toggle-files-only]    Toggle whether the menu displays only file buffers.
-\\[Buffer-menu-bury]    Bury the buffer listed on this line."
+\\[Buffer-menu-bury]    Bury the buffer listed on this line.
+\\[Buffer-menu-delete-by-major-mode]  Prompt for major mode name and
+     delete all buffers in that mode.
+\\[Buffer-menu-delete-by-buffer-name-regexp]  Prompt for regexp and
+     mark for deletion all buffers with matching names.
+\\[Buffer-menu-delete-by-file-name-regexp]  Prompt for regexp and
+     mark for deletion all buffers with mathing file names."
   (set (make-local-variable 'buffer-stale-function)
        (lambda (&optional _noconfirm) 'fast))
   (add-hook 'tabulated-list-revert-hook 'list-buffers--refresh nil t))
@@ -270,7 +282,7 @@ Menu."
   (interactive "P")
   (switch-to-buffer (list-buffers-noselect arg))
   (message
-   "Commands: d, s, x, u; f, o, 1, 2, m, v; ~, %%; q to quit; ? for help."))
+   "Commands: d, s, x, u; f, o, 1, 2, m, v; ~, %%; D m, D b, D f; q to quit; ? for help."))
 
 (defun buffer-menu-other-window (&optional arg)
   "Display the Buffer Menu in another window.
@@ -282,7 +294,7 @@ ARG, show only buffers that are visiting files."
   (interactive "P")
   (switch-to-buffer-other-window (list-buffers-noselect arg))
   (message
-   "Commands: d, s, x, u; f, o, 1, 2, m, v; ~, %%; q to quit; ? for help."))
+   "Commands: d, s, x, u; f, o, 1, 2, m, v; ~, %%; D m, D b, D f; q to quit; ? for help."))
 
 ;;;###autoload
 (defun list-buffers (&optional arg)
@@ -651,6 +663,70 @@ means list those buffers and no others."
     (setq tabulated-list-entries (nreverse entries)))
   (tabulated-list-init-header))
 
+(defun Buffer-menu--delete-by-predicate (pred)
+  (let ((count 0))
+    (save-excursion
+      (Buffer-menu-beginning)
+      (while (not (eobp))
+	(let ((buffer (tabulated-list-get-id))
+	      (entry (tabulated-list-get-entry)))
+	  (if (funcall pred buffer entry)
+	      (progn
+		(Buffer-menu-delete 1)
+		(setq count (1+ count)))
+	    (forward-line 1)))))
+    (message "Marked %d buffers" count)))
+
+(defun Buffer-menu-delete-by-major-mode (mode)
+  "Mark for deletion all buffers whose mode symbol's name is MODE.  Interactively,
+ask for the MODE, providing completion."
+  (interactive
+   (list
+    (let ((this-line-buffer (tabulated-list-get-id))
+	  modes)
+      (save-excursion
+	(Buffer-menu-beginning)
+	(while (not (eobp))
+	  (let ((buffer (tabulated-list-get-id)))
+	    (add-to-list 'modes (with-current-buffer buffer
+				  (symbol-name major-mode))))
+	  (forward-line 1)))
+      (completing-read
+       "Buffer major mode: " (sort modes (function string<))
+       nil t nil nil
+       (when this-line-buffer
+	 (with-current-buffer this-line-buffer
+	   (symbol-name major-mode)))))))
+  (or mode (error "MODE is nil"))
+  (Buffer-menu--delete-by-predicate
+   (lambda (buf entry)
+     (string= (with-current-buffer buf
+		(symbol-name major-mode))
+	      mode))))
+
+(defun Buffer-menu-delete-by-buffer-name-regexp (regexp)
+  "Mark for deletion all buffers whose buffer name matches REGEXP"
+  (interactive
+   (list
+    (read-regexp "Buffer name regexp: " 'regexp-history-last
+		 Buffer-menu--regexp-history)))
+  (or regexp (error "REGEXP is nil"))
+  (Buffer-menu--delete-by-predicate
+   (lambda (buf entry)
+     (string-match regexp (buffer-name buf)))))
+
+(defun Buffer-menu-delete-by-file-name-regexp (regexp)
+  "Mark for deletion all buffers whose file name matches REGEXP"
+  (interactive
+   (list
+    (read-regexp "File name regexp: " 'regexp-history-last
+		 Buffer-menu--regexp-history)))
+  (or regexp (error "REGEXP is nil"))
+  (Buffer-menu--delete-by-predicate
+   (lambda (buf entry)
+     (and (buffer-file-name buf)
+	  (string-match regexp (buffer-file-name buf))))))
+
 (defun tabulated-list-entry-size-> (entry1 entry2)
   (> (string-to-number (aref (cadr entry1) 4))
      (string-to-number (aref (cadr entry2) 4))))